การค้นหาการตั้งค่าช่วยให้คุณค้นหาและเปลี่ยนการตั้งค่าที่เฉพาะเจาะจงในแอปการตั้งค่ายานยนต์ได้อย่างรวดเร็วและง่ายดายโดยไม่ต้องไปยังส่วนต่างๆ ในเมนูของแอป การค้นหาเป็นวิธีที่มีประสิทธิภาพมากที่สุดในการค้นหาการตั้งค่าที่เฉพาะเจาะจง โดยค่าเริ่มต้น การค้นหาจะค้นหาการตั้งค่า AOSP เท่านั้น การตั้งค่าเพิ่มเติมไม่ว่าจะมีการแทรกหรือไม่ก็ตาม จะต้องมีการเปลี่ยนแปลงเพิ่มเติมเพื่อให้ระบบจัดทำดัชนี
ข้อกำหนด
ข้อมูลต้องมาจากแหล่งที่มาต่อไปนี้เพื่อให้การตั้งค่าได้รับการจัดทําดัชนีโดย "การค้นหาการตั้งค่า"
SearchIndexable
ที่อยู่ภายในCarSettings
- แอประดับระบบ
กําหนดข้อมูล
ฟิลด์ทั่วไป
Key
. (ต้องระบุ) คีย์สตริงที่มนุษย์อ่านได้ที่ไม่ซ้ำกันเพื่อระบุผลลัพธ์IconResId
. ไม่บังคับ หากไอคอนปรากฏในแอปข้างผลการค้นหา ให้เพิ่มรหัสทรัพยากร หากไม่ ให้ข้ามIntentAction
. ต้องระบุหากไม่ได้กําหนดIntentTargetPackage
หรือIntentTargetClass
กำหนดการดำเนินการที่ผลการค้นหาต้องการดำเนินการIntentTargetPackage
ต้องระบุหากไม่ได้กำหนดIntentAction
กำหนดแพ็กเกจที่จะเป็นปลายทางของ Intent ของผลการค้นหาIntentTargetClass
ต้องระบุหากไม่ได้กำหนดIntentAction
กำหนดคลาส (กิจกรรม) ที่จะเป็นปลายทางของ Intent ของผลการค้นหา
SearchIndexableResource
เท่านั้น
XmlResId
(ต้องระบุ) กําหนดรหัสแหล่งข้อมูล XML ของหน้าเว็บที่มีผลการค้นหาที่จะจัดทําดัชนี
SearchIndexableRaw
เท่านั้น
Title
. (ต้องระบุ) ชื่อของผลการค้นหาSummaryOn
(ไม่บังคับ) สรุปผลการค้นหาKeywords
. (ไม่บังคับ) รายการคำที่เชื่อมโยงกับผลการค้นหา จับคู่คำค้นหากับผลการค้นหาScreenTitle
(ไม่บังคับ) ชื่อหน้าเว็บที่มีผลการค้นหา
ซ่อนข้อมูล
ผลการค้นหาแต่ละรายการจะปรากฏใน Search เว้นแต่จะระบุไว้เป็นอย่างอื่น แม้ว่าระบบจะแคชผลการค้นหาแบบคงที่ไว้ แต่ระบบจะดึงข้อมูลรายการคีย์ที่ไม่สามารถจัดทำดัชนีได้ใหม่ทุกครั้งที่เปิดการค้นหา สาเหตุในการซ่อนผลลัพธ์อาจรวมถึง
- ทำซ้ำ เช่น ปรากฏในหลายหน้า
- แสดงแบบมีเงื่อนไขเท่านั้น เช่น แสดงเฉพาะการตั้งค่าอินเทอร์เน็ตมือถือเมื่อใส่ซิมอยู่)
- หน้าเทมเพลต เช่น หน้ารายละเอียดของแอปแต่ละแอป
- การตั้งค่าต้องมีบริบทมากกว่าชื่อและคำบรรยาย เช่น การตั้งค่า "การตั้งค่า" ซึ่งเกี่ยวข้องกับชื่อหน้าจอเท่านั้น
หากต้องการซ่อนการตั้งค่า ผู้ให้บริการหรือ SEARCH_INDEX_DATA_PROVIDER
ควรส่งคีย์ของผลการค้นหาจาก getNonIndexableKeys
ระบบจะแสดงคีย์เสมอ (กรณีหน้าเว็บที่ซ้ำกันหรือใช้เทมเพลต) หรือเพิ่มแบบมีเงื่อนไข (กรณีไม่มีข้อมูลอุปกรณ์เคลื่อนที่)
ดัชนีแบบคงที่
ใช้ดัชนีแบบคงที่หากข้อมูลดัชนีเหมือนกันเสมอ เช่น ชื่อและข้อมูลสรุปของ XML หรือข้อมูลดิบแบบฮาร์ดโค้ด ระบบจะจัดทำดัชนีข้อมูลแบบคงที่เพียงครั้งเดียวเมื่อเปิดตัวการค้นหาการตั้งค่าเป็นครั้งแรก
สําหรับการตั้งค่าที่จัดทำดัชนีได้ ให้ใช้ getXmlResourcesToIndex
และ/หรือ getRawDataToIndex
สำหรับการตั้งค่าที่แทรก ให้ใช้เมธอด queryXmlResources
และ/หรือ queryRawData
ดัชนีแบบไดนามิก
หากอัปเดตข้อมูลที่จัดทำดัชนีได้ตามความเหมาะสม ให้ใช้วิธีการแบบไดนามิกเพื่อจัดทำดัชนีข้อมูล การค้นหาการตั้งค่าจะอัปเดตรายการแบบไดนามิกนี้เมื่อเปิดใช้งาน
สำหรับรายการที่จัดทําดัชนีได้ภายในการตั้งค่า ให้ใช้ getDynamicRawDataToIndex
สําหรับการตั้งค่าที่แทรก ให้ใช้ queryDynamicRawData methods
ดัชนีในการตั้งค่ารถ
หากต้องการจัดทำดัชนีการตั้งค่าต่างๆ ให้รวมอยู่ในฟีเจอร์การค้นหา โปรดดูผู้ให้บริการเนื้อหา แพ็กเกจ SettingsLib
และ android.provider
มีการกําหนดอินเทอร์เฟซและคลาสนามธรรมไว้แล้วเพื่อขยายการให้บริการรายการใหม่ที่จะจัดทําดัชนี ในการตั้งค่า AOSP ระบบจะใช้การใช้งานคลาสเหล่านี้เพื่อจัดทำดัชนีผลลัพธ์ อินเทอร์เฟซหลักที่จะดำเนินการคือ SearchIndexablesProvider
ซึ่ง SettingsIntelligence
ใช้เพื่อจัดทำดัชนีข้อมูล
public abstract class SearchIndexablesProvider extends ContentProvider { public abstract Cursor queryXmlResources(String[] projection); public abstract Cursor queryRawData(String[] projection); public abstract Cursor queryNonIndexableKeys(String[] projection); }
ในทางทฤษฎีแล้ว แต่ละข้อมูลโค้ดสามารถเพิ่มลงในผลการค้นหาใน SearchIndexablesProvider
ได้ ซึ่งในกรณีนี้ SettingsIntelligence
จะเป็นเนื้อหา หากต้องการให้กระบวนการนี้ง่ายต่อการบำรุงรักษาเพื่อเพิ่มข้อมูลโค้ดใหม่ ให้ใช้SettingsLib
การสร้างโค้ดของ SearchIndexableResources
สำหรับการตั้งค่ารถยนต์โดยเฉพาะ แต่ละส่วนที่จัดทำดัชนีได้จะมีคำอธิบายประกอบเป็น @SearchIndexable
จากนั้นจะมีฟิลด์ SearchIndexProvider
แบบคงที่ซึ่งให้ข้อมูลที่เกี่ยวข้องกับส่วนที่ระบุ ข้อมูลโค้ดที่มีคำอธิบายประกอบแต่ไม่มี SearchIndexProvider
จะทำให้เกิดข้อผิดพลาดในการคอมไพล์
interface SearchIndexProvider { List<SearchIndexableResource> getXmlResourcesToIndex(Context context, boolean enabled); List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled); List<SearchIndexableRaw> getDynamicRawDataToIndex(Context context, boolean enabled); List<String> getNonIndexableKeys(Context context); }
จากนั้นระบบจะเพิ่มข้อมูลโค้ดที่ตัดตอนมาทั้งหมดเหล่านี้ลงในคลาส SearchIndexableResourcesAuto
ที่สร้างขึ้นโดยอัตโนมัติ ซึ่งเป็น Wrapper แบบบางรอบรายการช่อง SearchIndexProvider
ของข้อมูลโค้ดที่ตัดตอนมาทั้งหมด
ระบบรองรับการระบุเป้าหมายที่เฉพาะเจาะจงสำหรับการกำกับเนื้อหา (เช่น Auto, TV และ Wear) แต่การกำกับเนื้อหาส่วนใหญ่จะยังคงเป็นค่าเริ่มต้น (All
)
ใน Use Case นี้ ไม่จำเป็นต้องระบุเป้าหมาย Auto ดังนั้นจึงยังคงเป็น All
การใช้งานพื้นฐานของ SearchIndexProvider
มีไว้เพื่อให้เพียงพอสำหรับข้อมูลโค้ดส่วนใหญ่
public class CarBaseSearchIndexProvider implements Indexable.SearchIndexProvider { private static final Logger LOG = new Logger(CarBaseSearchIndexProvider.class); private final int mXmlRes; private final String mIntentAction; private final String mIntentClass; public CarBaseSearchIndexProvider(@XmlRes int xmlRes, String intentAction) { mXmlRes = xmlRes; mIntentAction = intentAction; mIntentClass = null; } public CarBaseSearchIndexProvider(@XmlRes int xmlRes, @NonNull Class intentClass) { mXmlRes = xmlRes; mIntentAction = null; mIntentClass = intentClass.getName(); } @Override public List<SearchIndexableResource> getXmlResourcesToIndex(Context context, boolean enabled) { SearchIndexableResource sir = new SearchIndexableResource(context); sir.xmlResId = mXmlRes; sir.intentAction = mIntentAction; sir.intentTargetPackage = context.getPackageName(); sir.intentTargetClass = mIntentClass; return Collections.singletonList(sir); } @Override public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) { return null; } @Override public List<SearchIndexableRaw> getDynamicRawDataToIndex(Context context, boolean enabled) { return null; } @Override public List<String> getNonIndexableKeys(Context context) { if (!isPageSearchEnabled(context)) { try { return PreferenceXmlParser.extractMetadata(context, mXmlRes, FLAG_NEED_KEY) .stream() .map(bundle -> bundle.getString(METADATA_KEY)) .collect(Collectors.toList()); } catch (IOException | XmlPullParserException e) { LOG.w("Error parsing non-indexable XML - " + mXmlRes); } } return null; } /** * Returns true if the page should be considered in search query. If return false, entire page is suppressed during search query. */ protected boolean isPageSearchEnabled(Context context) { return true; } }
จัดทำดัชนีข้อมูลโค้ดใหม่
การออกแบบนี้ช่วยให้คุณเพิ่ม SettingsFragment
ใหม่เพื่อรับการจัดทําดัชนีได้ง่าย โดยทั่วไปแล้วการอัปเดต 2 บรรทัดจะระบุ XML ของข้อมูลโค้ดและระบุความตั้งใจที่จะปฏิบัติตาม โดยใช้ WifiSettingsFragment
เป็นตัวอย่าง
@SearchIndexable public class WifiSettingsFragment extends SettingsFragment { [...] public static final CarBaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new CarBaseSearchIndexProvider(R.xml.wifi_list_fragment, Settings.ACTION_WIFI_SETTINGS); }
การใช้งาน SearchIndexablesProvider
ของ AAOS ซึ่งใช้ SearchIndexableResources
และทำการแปลจาก SearchIndexProviders
เป็นสคีมาฐานข้อมูลสําหรับ SettingsIntelligence
แต่ไม่สนใจว่าระบบจะจัดทําดัชนีส่วนย่อยใด SettingsIntelligence
รองรับผู้ให้บริการจํานวนเท่าใดก็ได้ คุณจึงสร้างผู้ให้บริการใหม่เพื่อรองรับ Use Case ที่เฉพาะเจาะจงได้ ซึ่งช่วยให้ผู้ให้บริการแต่ละรายมีความเชี่ยวชาญและมุ่งเน้นที่ผลลัพธ์ที่มีโครงสร้างคล้ายกัน ค่ากําหนดที่กำหนดไว้ใน PreferenceScreen
ของข้อมูลโค้ดต้องกำหนดคีย์ที่ไม่ซ้ำกันให้กับแต่ละค่าเพื่อให้ระบบจัดทำดัชนีได้ นอกจากนี้ PreferenceScreen
จะต้องมีการกำหนดคีย์เพื่อให้ระบบจัดทำดัชนีชื่อหน้าจอ
ตัวอย่างดัชนี
ในบางกรณี ข้อมูลโค้ดอาจไม่มีการดำเนินการตาม Intent ที่เฉพาะเจาะจงเชื่อมโยงอยู่ ในกรณีเช่นนี้ คุณสามารถส่งคลาสกิจกรรมไปยัง Intent CarBaseSearchIndexProvider
โดยใช้คอมโพเนนต์แทนการดำเนินการ แต่กิจกรรมดังกล่าวยังคงต้องอยู่ในไฟล์ Manifest และจะต้องส่งออก
@SearchIndexable public class LanguagesAndInputFragment extends SettingsFragment { [...] public static final CarBaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new CarBaseSearchIndexProvider(R.xml.languages_and_input_fragment, LanguagesAndInputActivity.class); }
ในบางกรณีพิเศษ คุณอาจต้องลบล้างวิธีการบางอย่างของ CarBaseSearchIndexProvider
เพื่อให้ระบบจัดทำดัชนีผลลัพธ์ที่ต้องการ ตัวอย่างเช่น ใน NetworkAndInternetFragment
ระบบจะไม่จัดทำดัชนีค่ากําหนดที่เกี่ยวข้องกับเครือข่ายมือถือในอุปกรณ์ที่ไม่มีเครือข่ายมือถือ ในกรณีนี้ ให้ลบล้างเมธอด getNonIndexableKeys
และทําเครื่องหมายคีย์ที่เหมาะสมเป็นไม่สามารถจัดทำดัชนีได้เมื่ออุปกรณ์ไม่มีเครือข่ายมือถือ
@SearchIndexable public class NetworkAndInternetFragment extends SettingsFragment { [...] public static final CarBaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new CarBaseSearchIndexProvider(R.xml.network_and_internet_fragment, Settings.Panel.ACTION_INTERNET_CONNECTIVITY) { @Override public List<String> getNonIndexableKeys(Context context) { if (!NetworkUtils.hasMobileNetwork( context.getSystemService(ConnectivityManager.class))) { List<String> nonIndexableKeys = new ArrayList<>(); nonIndexableKeys.add(context.getString( R.string.pk_mobile_network_settings_entry)); nonIndexableKeys.add(context.getString( R.string.pk_data_usage_settings_entry)); return nonIndexableKeys; } return null; } }; }
ระบบอาจลบล้างวิธีการอื่นๆ ของ CarBaseSearchIndexProvider
เพื่อรวมข้อมูลอื่นๆ ที่จัดทำดัชนีได้ เช่น ข้อมูลดิบแบบคงที่และแบบไดนามิก ทั้งนี้ขึ้นอยู่กับความต้องการของแต่ละข้อมูล
@SearchIndexable public class RawIndexDemoFragment extends SettingsFragment { public static final String KEY_CUSTOM_RESULT = "custom_result_key"; [...] public static final CarBaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new CarBaseSearchIndexProvider(R.xml.raw_index_demo_fragment, RawIndexDemoActivity.class) { @Override public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) { List<SearchIndexableRaw> rawData = new ArrayList<>(); SearchIndexableRaw customResult = new SearchIndexableRaw(context); customResult.key = KEY_CUSTOM_RESULT; customResult.title = context.getString(R.string.my_title); customResult.screenTitle = context.getString(R.string.my_screen_title); rawData.add(customResult); return rawData; } @Override public List<SearchIndexableRaw> getDynamicRawDataToIndex(Context context, boolean enabled) { List<SearchIndexableRaw> rawData = new ArrayList<>(); SearchIndexableRaw customResult = new SearchIndexableRaw(context); if (hasIndexData()) { customResult.key = KEY_CUSTOM_RESULT; customResult.title = context.getString(R.string.my_title); customResult.screenTitle = context.getString(R.string.my_screen_title); } rawData.add(customResult); return rawData; } }; }
การตั้งค่าการแทรกดัชนี
วิธีแทรกการตั้งค่าที่จะได้รับการจัดทำดัชนี
- กำหนด
SearchIndexablesProvider
สําหรับแอปโดยขยายคลาสandroid.provider.SearchIndexablesProvider
- อัปเดต
AndroidManifest.xml
ของแอปกับผู้ให้บริการในขั้นตอนที่ 1 รูปแบบคือ<provider android:name="PROVIDER_CLASS_NAME" android:authorities="PROVIDER_AUTHORITY" android:multiprocess="false" android:grantUriPermissions="true" android:permission="android.permission.READ_SEARCH_INDEXABLES" android:exported="true"> <intent-filter> <action android:name="android.content.action.SEARCH_INDEXABLES_PROVIDER" /> </intent-filter> </provider>
-
เพิ่มข้อมูลที่จัดทำดัชนีได้ลงในผู้ให้บริการ การติดตั้งใช้งานขึ้นอยู่กับความต้องการของแอป โดยสามารถจัดทำดัชนีข้อมูลได้ 2 ประเภท ได้แก่
SearchIndexableResource
และSearchIndexableRaw
ตัวอย่าง SearchIndexablesProvider
public class SearchDemoProvider extends SearchIndexablesProvider { /** * Key for Auto brightness setting. */ public static final String KEY_AUTO_BRIGHTNESS = "auto_brightness"; /** * Key for my magic preference. */ public static final String KEY_MY_PREFERENCE = "my_preference_key"; /** * Key for my custom search result. */ public static final String KEY_CUSTOM_RESULT = "custom_result_key"; private String mPackageName; @Override public boolean onCreate() { mPackageName = getContext().getPackageName(); return true; } @Override public Cursor queryXmlResources(String[] projection) { MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS); cursor.addRow(getResourceRow(R.xml.demo_xml)); return cursor; } @Override public Cursor queryRawData(String[] projection) { MatrixCursor cursor = new MatrixCursor(INDEXABLES_RAW_COLUMNS); Context context = getContext(); Object[] raw = new Object[INDEXABLES_RAW_COLUMNS.length]; raw[COLUMN_INDEX_RAW_TITLE] = context.getString(R.string.my_title); raw[COLUMN_INDEX_RAW_SUMMARY_ON] = context.getString(R.string.my_summary); raw[COLUMN_INDEX_RAW_KEYWORDS] = context.getString(R.string.my_keywords); raw[COLUMN_INDEX_RAW_SCREEN_TITLE] = context.getString(R.string.my_screen_title); raw[COLUMN_INDEX_RAW_KEY] = KEY_CUSTOM_RESULT; raw[COLUMN_INDEX_RAW_INTENT_ACTION] = Intent.ACTION_MAIN; raw[COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE] = mPackageName; raw[COLUMN_INDEX_RAW_INTENT_TARGET_CLASS] = MyDemoFragment.class.getName(); cursor.addRow(raw); return cursor; } @Override public Cursor queryDynamicRawData(String[] projection) { MatrixCursor cursor = new MatrixCursor(INDEXABLES_RAW_COLUMNS); DemoObject object = getDynamicIndexData(); Object[] raw = new Object[INDEXABLES_RAW_COLUMNS.length]; raw[COLUMN_INDEX_RAW_KEY] = object.key; raw[COLUMN_INDEX_RAW_TITLE] = object.title; raw[COLUMN_INDEX_RAW_KEYWORDS] = object.keywords; raw[COLUMN_INDEX_RAW_INTENT_ACTION] = object.intentAction; raw[COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE] = object.mPackageName; raw[COLUMN_INDEX_RAW_INTENT_TARGET_CLASS] = object.className; cursor.addRow(raw); return cursor; } @Override public Cursor queryNonIndexableKeys(String[] projection) { MatrixCursor cursor = new MatrixCursor(NON_INDEXABLES_KEYS_COLUMNS); cursor.addRow(getNonIndexableRow(KEY_AUTO_BRIGHTNESS)); if (!Utils.isMyPreferenceAvailable) { cursor.addRow(getNonIndexableRow(KEY_MY_PREFERENCE)); } return cursor; } private Object[] getResourceRow(int xmlResId) { Object[] row = new Object[INDEXABLES_XML_RES_COLUMNS.length]; row[COLUMN_INDEX_XML_RES_RESID] = xmlResId; row[COLUMN_INDEX_XML_RES_ICON_RESID] = 0; row[COLUMN_INDEX_XML_RES_INTENT_ACTION] = Intent.ACTION_MAIN; row[COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE] = mPackageName; row[COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS] = SearchResult.class.getName(); return row; } private Object[] getNonIndexableRow(String key) { final Object[] ref = new Object[NON_INDEXABLES_KEYS_COLUMNS.length]; ref[COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE] = key; return ref; } private DemoObject getDynamicIndexData() { if (hasIndexData) { DemoObject object = new DemoObject(); object.key = "demo key"; object.title = "demo title"; object.keywords = "demo, keywords"; object.intentAction = "com.demo.DYNAMIC_INDEX"; object.packageName = "com.demo"; object.className = "DemoClass"; return object; } } }