การค้นหาการตั้งค่าช่วยให้คุณค้นหาและเปลี่ยน การตั้งค่าในแอปการตั้งค่ายานยนต์โดยไม่ต้องไปยังเมนูของแอปเพื่อ หาเจอ การค้นหาเป็นวิธีที่มีประสิทธิภาพมากที่สุดในการค้นหาการตั้งค่าที่ต้องการ โดยค่าเริ่มต้น Search จะค้นหาเฉพาะการตั้งค่า AOSP เท่านั้น การตั้งค่าเพิ่มเติม ไม่ว่าจะแทรกหรือไม่ก็ตาม ต้องมีการเปลี่ยนแปลงเพิ่มเติมเพื่อให้จัดทำดัชนีได้
ข้อกำหนด
ข้อมูลต้องมาจากแหล่งที่มาต่อไปนี้เพื่อให้การตั้งค่าจัดทำดัชนีได้ด้วยการค้นหาการตั้งค่า
- ส่วนย่อย
SearchIndexable
ภายในCarSettings
- แอประดับระบบ
กำหนดข้อมูล
ฟิลด์ทั่วไปมีดังนี้
Key
(ต้องระบุ) คีย์สตริงที่ไม่ซ้ำกันที่มนุษย์อ่านได้เพื่อระบุผลลัพธ์IconResId
ไม่บังคับ หากไอคอนปรากฏในแอปข้าง ให้เพิ่มรหัสทรัพยากร ไม่เช่นนั้นก็ไม่ต้องสนใจIntentAction
ต้องระบุหากIntentTargetPackage
หรือ ไม่ได้กำหนดIntentTargetClass
กำหนดการดำเนินการที่ผลการค้นหา ความตั้งใจที่จะทำตามIntentTargetPackage
ต้องระบุหากIntentAction
ไม่ใช่ กำหนดไว้ กำหนดแพ็กเกจที่ผลการค้นหาต้องการแก้ไขIntentTargetClass
ต้องระบุหากIntentAction
ไม่ใช่ กำหนดไว้ กำหนดคลาส (กิจกรรม) ที่จุดประสงค์ของผลการค้นหาจะเปลี่ยนแปลง
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); }
จากนั้น Fragment ทั้งหมดนี้จะถูกเพิ่มลงในส่วนที่สร้างโดยอัตโนมัติ
SearchIndexableResourcesAuto
คลาส ซึ่งเป็น Wrapper แบบบาง
รอบๆ รายการของช่อง SearchIndexProvider
สำหรับส่วนย่อยทั้งหมด
การสนับสนุนมีให้บริการสำหรับการระบุเป้าหมายเฉพาะสำหรับคำอธิบายประกอบ (เช่น
Auto, TV และ Wear) แต่คำอธิบายประกอบส่วนใหญ่จะทิ้งไว้โดยค่าเริ่มต้น (All
)
ในกรณีการใช้งานนี้ ไม่จำเป็นต้องมีเฉพาะในการระบุเป้าหมายอัตโนมัติ ระบบจึงยังคง
ในชื่อ 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); }
การติดตั้งใช้งาน AAOS ของ SearchIndexablesProvider
ใช้ SearchIndexableResources
และแปลจาก
SearchIndexProviders
ลงในสคีมาฐานข้อมูลสำหรับ
SettingsIntelligence
แต่ไม่จำเป็นต้องเข้าใจความหมายของส่วนย่อย
ได้รับการจัดทำดัชนี SettingsIntelligence
รองรับกี่รายการก็ได้
ผู้ให้บริการเครือข่าย เพื่อให้สามารถสร้างผู้ให้บริการใหม่ๆ เพื่อรองรับการใช้งานเฉพาะทางได้
โดยช่วยให้แต่ละรายมีความเฉพาะเจาะจงและมุ่งเน้นไปยังผลลัพธ์
เหล่านี้ ค่ากำหนดที่ระบุไว้ใน PreferenceScreen
สำหรับส่วนย่อยแล้ว จะต้องมีการกำหนดคีย์ที่ไม่ซ้ำกันให้แต่ละคีย์เพื่อให้
จัดทำดัชนีแล้ว นอกจากนี้ PreferenceScreen
ต้องมีคีย์ด้วย
ที่กำหนดให้กับชื่อหน้าจอที่จะจัดทำดัชนี
ตัวอย่างดัชนี
ในบางกรณี ส่วนย่อยอาจไม่มีการดำเนินการผ่าน Intent ที่เฉพาะเจาะจง
ด้วย ในกรณีเช่นนี้ คุณสามารถส่งคลาสกิจกรรมไปยัง
CarBaseSearchIndexProvider
Intent ที่ใช้คอมโพเนนต์แทน
การกระทำ การดำเนินการนี้ยังคงต้องมีกิจกรรมในไฟล์ 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; } } }