設定搜尋功能可讓你快速輕鬆地搜尋及變更 不必從應用程式選單前往 Automotive 設定頁面 並找到想要的程式。如要找出特定設定,最有效的方法就是使用搜尋功能。根據預設 搜尋只會找到 Android 開放原始碼計畫設定。其他設定 (無論插入與否), 需要進行其他變更才能編入索引
需求條件
如要讓設定搜尋可建立索引的設定,資料必須來自:
CarSettings
中的SearchIndexable
片段。- 系統層級應用程式。
定義資料
常用欄位:
Key
。(必要) 不重複的人類可讀字串鍵,用於識別結果。IconResId
。選用:如果應用程式顯示的圖示位於 結果,然後加入資源 ID,否則請忽略。IntentAction
。如果IntentTargetPackage
或 未定義IntentTargetClass
。定義搜尋結果的動作 最終意圖IntentTargetPackage
。如果不是IntentAction
,則為必填 定義搜尋結果意圖要解析的套件。IntentTargetClass
。如果不是IntentAction
,則為必填 定義搜尋結果意圖要解析的類別 (活動)。
僅限 SearchIndexableResource
:
XmlResId
。(必要) 定義內含 將結果編入索引
僅限 SearchIndexableRaw
:
Title
。(必要) 搜尋結果的標題。SummaryOn
。(選用) 搜尋結果摘要。Keywords
。(選用) 與搜尋結果相關聯的字詞清單。 比對查詢內容。ScreenTitle
。(選填) 搜尋結果所在的網頁標題。
隱藏資料
除非另有標示,否則每項搜尋結果都會顯示在 Google 搜尋中。靜態時 快取搜尋結果,且每次都會擷取無法建立索引的鍵清單 則已開啟。隱藏結果的原因可能包括:
- 重複。舉例來說,廣告出現在多個網頁上。
- 只在有條件的情況下顯示。例如,僅顯示行動數據設定 (如有 SIM 卡插入)。
- 範本頁面。例如個別應用程式的詳細資料頁面。
- 設定需要的背景資訊比標題和字幕更廣泛。適用對象 例如「設定」設定,該屬性只會與畫面標題相關。
如要隱藏設定,你的供應商或 SEARCH_INDEX_DATA_PROVIDER
必須
傳回來自 getNonIndexableKeys
的搜尋結果鍵。金鑰可以
一律傳回 (重複、範本網頁案例) 或有條件新增 (無行動版)
資料案例)。
靜態索引
如果索引資料始終相同,請使用靜態索引。例如 XML 資料或硬式編碼原始資料的摘要靜態資料已編入索引 首次啟動設定搜尋功能時
如果是設定中的可建立索引項目,請實作 getXmlResourcesToIndex
和/或 getRawDataToIndex
如果是插入的設定,請導入
queryXmlResources
和/或 queryRawData
方法。
動態索引
如果可建立索引的資料可以據此更新,請使用動態方法 索引資料。「設定搜尋」會在這個動態清單啟動時更新。
針對設定中可建立索引的項目,請實作 getDynamicRawDataToIndex
。
如果是插入的設定,請實作 queryDynamicRawData methods
。
車輛設定中的索引
如要為要加入搜尋功能中的不同設定建立索引,請參閱
內容
供應商。《SettingsLib
》和《android.provider
》
套件已定義介面和抽象類別
提供要編入索引的新項目在 Android 開放原始碼計畫設定中,
類別可用來將結果編入索引。要完成的主要介面是
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
進行索引,通常會有兩行更新,為片段和
要追蹤的意圖以 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
支援任意數量的任何數量
以便建立新的供應商,協助特殊用途
各自具備專業形象,專注於具有類似成果的結果
成本中心的架構PreferenceScreen
中定義的偏好設定
的每個片段都必須有專屬索引鍵,才能
已編入索引。此外,PreferenceScreen
必須具有索引鍵
。
索引範例
在某些情況下,片段可能沒有相關聯的特定意圖動作
。在此情況下,您可以將活動類別傳入
CarBaseSearchIndexProvider
意圖,而非使用元件
動作。這項操作仍須在資訊清單檔案中列出該活動
以便匯出
@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
類別。 - 使用步驟 1 中的供應商更新應用程式的
AndroidManifest.xml
。 格式如下: 敬上<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; } } }