การค้นหาการตั้งค่าทำให้คุณสามารถค้นหาและเปลี่ยนแปลงการตั้งค่าเฉพาะในแอปการตั้งค่ายานยนต์ได้อย่างรวดเร็วและง่ายดาย โดยไม่ต้องไปที่เมนูแอปเพื่อค้นหา การค้นหาเป็นวิธีที่มีประสิทธิภาพที่สุดในการค้นหาการตั้งค่าเฉพาะ ตามค่าเริ่มต้น การค้นหาจะค้นหาการตั้งค่า AOSP เท่านั้น การตั้งค่าเพิ่มเติม ไม่ว่าจะแทรกหรือไม่ก็ตาม จำเป็นต้องมีการเปลี่ยนแปลงเพิ่มเติมจึงจะจัดทำดัชนีได้
ความต้องการ
เพื่อให้การตั้งค่าจัดทำดัชนีได้โดยการค้นหาการตั้งค่า ข้อมูลต้องมาจาก:
-
SearchIndexable
ส่วนที่จัดทำดัชนีได้ภายในCarSettings
- แอประดับระบบ
กำหนดข้อมูล
ช่องทั่วไป:
-
Key
. ( จำเป็น ) คีย์สตริงที่มนุษย์สามารถอ่านได้เฉพาะเพื่อระบุผลลัพธ์ -
IconResId
ไม่บังคับ หากไอคอนปรากฏในแอปของคุณถัดจากผลลัพธ์ ให้เพิ่มรหัสทรัพยากร มิเช่นนั้นก็ไม่ต้องสนใจ -
IntentAction
. จำเป็น หากไม่ได้กำหนดIntentTargetPackage
หรือIntentTargetClass
กำหนดการดำเนินการที่จุดประสงค์ของผลลัพธ์การค้นหาจะดำเนินการ -
IntentTargetPackage
จำเป็น หากไม่ได้กำหนดIntentAction
กำหนดแพ็กเกจที่จุดประสงค์ของผลลัพธ์การค้นหาคือการแก้ไข -
IntentTargetClass
จำเป็น หากไม่ได้กำหนดIntentAction
กำหนดคลาส (กิจกรรม) ที่จุดประสงค์ของผลลัพธ์การค้นหาคือการแก้ไข
SearchIndexableResource
เท่านั้น:
-
XmlResId
( จำเป็น ) กำหนดรหัสทรัพยากร XML ของเพจที่มีผลลัพธ์ที่จะจัดทำดัชนี
SearchIndexableRaw
เท่านั้น:
-
Title
. ( จำเป็น ) ชื่อของผลการค้นหา -
SummaryOn
. ( ไม่บังคับ ) สรุปผลการค้นหา -
Keywords
( ไม่บังคับ ) รายการคำที่เกี่ยวข้องกับผลการค้นหา จับคู่คำค้นหากับผลลัพธ์ของคุณ -
ScreenTitle
. ( ไม่บังคับ ) ชื่อของหน้าพร้อมผลการค้นหาของคุณ
ซ่อนข้อมูล
ผลการค้นหาแต่ละรายการจะปรากฏในการค้นหา เว้นแต่จะมีการทำเครื่องหมายเป็นอย่างอื่น ในขณะที่ผลการค้นหาแบบคงที่ถูกแคชไว้ รายการใหม่ของคีย์ที่ไม่สามารถจัดทำดัชนีได้จะถูกเรียกค้นทุกครั้งที่เปิดการค้นหา เหตุผลในการซ่อนผลลัพธ์อาจรวมถึง:
- ทำซ้ำ. เช่น ปรากฏในหลายหน้า
- แสดงตามเงื่อนไขเท่านั้น เช่น แสดงเฉพาะการตั้งค่าข้อมูลมือถือเมื่อมีซิมการ์ด)
- หน้าเทมเพลต เช่น หน้ารายละเอียดของแต่ละแอป
- การตั้งค่าต้องการบริบทมากกว่าชื่อเรื่องและคำบรรยาย ตัวอย่างเช่น การตั้งค่า "การตั้งค่า" ซึ่งเกี่ยวข้องกับชื่อหน้าจอเท่านั้น
หากต้องการซ่อนการตั้งค่า ผู้ให้บริการของคุณหรือ 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
ที่สร้างขึ้นโดยอัตโนมัติ ซึ่งเป็นตัวห่อแบบบางรอบๆ รายการฟิลด์ 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
ใหม่ที่จะจัดทำดัชนีจึงเป็นเรื่องง่าย โดยทั่วไปจะเป็นการอัปเดตสองบรรทัดที่ให้ 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
โดยใช้ส่วนประกอบแทนการกระทำ ซึ่งยังคงต้องมีกิจกรรมปรากฏในไฟล์ 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>
- เพิ่มข้อมูลที่สามารถจัดทำดัชนีให้กับผู้ให้บริการของคุณ การใช้งานขึ้นอยู่กับความต้องการของแอป สามารถจัดทำดัชนีข้อมูลที่แตกต่างกันสองประเภท:
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; } } }