La ricerca nelle impostazioni consente di cercare e modificare in modo facile e veloce impostazioni nell'app Automotive Settings senza dover navigare nei menu delle app a trovarlo. La ricerca è il modo più efficace per trovare un'impostazione specifica. Per impostazione predefinita, la ricerca trova solo le impostazioni AOSP. Impostazioni aggiuntive, inserite o meno, richiedono ulteriori modifiche per poter essere indicizzati.
Requisiti
Affinché un'impostazione sia indicizzabile dalla ricerca Impostazioni, i dati devono provenire da:
- Frammento
SearchIndexable
all'interno diCarSettings
. - App a livello di sistema.
Definisci i dati
Campi comuni:
Key
. (Obbligatorio) Chiave di stringa univoca leggibile per identificare il risultato.IconResId
. Facoltativo Se nell'app viene visualizzata un'icona accanto a il risultato, quindi aggiungi l'ID risorsa, altrimenti ignoralo.IntentAction
. Obbligatorio seIntentTargetPackage
oIntentTargetClass
non è definito. Definisce l'azione che il risultato di ricerca è prendere.IntentTargetPackage
. Obbligatorio seIntentAction
non è definito. Definisce il pacchetto in cui deve risolvere l'intento del risultato di ricerca.IntentTargetClass
. Obbligatorio seIntentAction
non è definito. Definisce la classe (attività) in cui deve risolversi l'intento del risultato di ricerca.
Solo SearchIndexableResource
:
XmlResId
. (Obbligatorio) Definisce l'ID risorsa XML della pagina contenente i risultati da indicizzare.
Solo SearchIndexableRaw
:
Title
. (Obbligatorio) Titolo del risultato di ricerca.SummaryOn
. (Facoltativo) Riepilogo del risultato di ricerca.Keywords
. (Facoltativo) Elenco di parole associate al risultato di ricerca. Corrisponde al risultato della query.ScreenTitle
. (Facoltativo) Titolo della pagina con il risultato di ricerca.
Nascondi dati
Ogni risultato di ricerca viene visualizzato nella Ricerca, a meno che non sia contrassegnato in modo diverso. Quando il veicolo è statico i risultati di ricerca vengono memorizzati nella cache e ogni volta viene recuperato un nuovo elenco di chiavi non indicizzabili si apre la ricerca. I motivi per nascondere i risultati possono includere:
- Duplica. Ad esempio, appare su più pagine.
- Mostrata solo in base alle condizioni. Ad esempio, mostra solo le impostazioni dei dati mobili quando è presente una scheda SIM).
- Pagina basata su modelli. ad esempio una pagina dei dettagli di una singola app.
- L'impostazione richiede più contesto rispetto a un titolo e un sottotitolo. Per ad esempio "Impostazioni" , che è pertinente solo al titolo della schermata.
Per nascondere un'impostazione, il tuo provider o SEARCH_INDEX_DATA_PROVIDER
deve
restituisce la chiave del risultato di ricerca di getNonIndexableKeys
. La chiave può
Deve essere sempre restituito (richieste di pagina duplicate o basate su modelli) o aggiunte in modo condizionale (nessuna
caso dati).
Indice statico
Utilizza l'indice statico se i dati dell'indice sono sempre gli stessi. Ad esempio, il titolo e un riepilogo dei dati XML o dei dati non elaborati hardcoded. I dati statici sono indicizzati solo una volta, quando viene avviata la ricerca nelle impostazioni.
Per gli elementi indicizzabili all'interno delle impostazioni, implementa getXmlResourcesToIndex
e/o getRawDataToIndex
. Per le impostazioni inserite, implementa la classe
metodi queryXmlResources
e/o queryRawData
.
Indice dinamico
Se i dati indicizzabili possono essere aggiornati di conseguenza, utilizza il metodo dinamico per indicizzare i dati. La ricerca nelle impostazioni aggiorna questo elenco dinamico quando viene lanciato.
Per gli elementi indicizzabili all'interno delle impostazioni, implementa getDynamicRawDataToIndex
.
Per le impostazioni inserite, implementa queryDynamicRawData methods
.
Indice in Impostazioni auto
Per indicizzare le diverse impostazioni da includere nella funzionalità di ricerca, vedi
Contenuti
di terze parti. SettingsLib
e android.provider
i pacchetti hanno già interfacce definite e classi astratte per l'estensione
fornendo nuove voci da indicizzare. Nelle impostazioni AOSP, le implementazioni
utilizzate per indicizzare i risultati. L'interfaccia principale da completare è
SearchIndexablesProvider
, utilizzata da
SettingsIntelligence
per indicizzare i dati.
public abstract class SearchIndexablesProvider extends ContentProvider { public abstract Cursor queryXmlResources(String[] projection); public abstract Cursor queryRawData(String[] projection); public abstract Cursor queryNonIndexableKeys(String[] projection); }
In teoria, ogni frammento potrebbe essere aggiunto ai risultati in
SearchIndexablesProvider
, nel qual caso SettingsIntelligence
sarebbero contenuti. Per semplificare la gestione del processo e aggiungere nuovi frammenti,
utilizza la generazione del codice SettingsLib
di SearchIndexableResources
.
Specifica per le Impostazioni auto, ogni frammento indicizzabile è annotato con
@SearchIndexable
e ha un valore SearchIndexProvider
statico
campo che fornisce i dati pertinenti per quel frammento. Frammenti con
l'annotazione, ma manca un risultato SearchIndexProvider
in una compilazione
.
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); }
Tutti questi frammenti vengono quindi aggiunti ai campi
SearchIndexableResourcesAuto
, che è un wrapper sottile
intorno all'elenco di SearchIndexProvider
campi per tutti i frammenti.
Viene fornita assistenza per specificare una destinazione specifica per un'annotazione (ad esempio
Auto, TV e Wear), tuttavia la maggior parte delle annotazioni rimane sul valore predefinito (All
).
In questo caso d'uso, non esiste alcuna esigenza specifica di specificare il target automatico, quindi rimane
come All
. L'implementazione di base di SearchIndexProvider
è pensato per essere sufficiente per la maggior parte dei frammenti.
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; } }
Indicizza un nuovo frammento
Con questo design, è relativamente facile aggiungere un nuovo SettingsFragment
da indicizzare, di solito un aggiornamento di due righe che fornisce il codice XML per il frammento e
l'intenzione di essere seguiti. Con WifiSettingsFragment
come esempio:
@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); }
L'implementazione AAOS di SearchIndexablesProvider
, che
utilizza SearchIndexableResources
ed esegue la traduzione da
SearchIndexProviders
nello schema del database per
SettingsIntelligence
, ma è indipendente da cosa sono i frammenti
in fase di indicizzazione. SettingsIntelligence
supporta un numero illimitato di
così da crearne di nuovi per supportare l'uso specializzato
di casi diversi, in modo che ciascuno di loro sia specializzato e si concentri su risultati
le nostre strutture. Le preferenze definite nelle PreferenceScreen
ogni frammento deve avere una chiave unica assegnata per poter essere
indicizzati. Inoltre, PreferenceScreen
deve avere una chiave
assegnato per indicizzare il titolo della schermata.
Esempi di indice
In alcuni casi, a un frammento potrebbe non essere associata un'azione di intent specifica
con essa. In questi casi, è possibile passare la classe di attività al
CarBaseSearchIndexProvider
intent che utilizza un componente anziché
un'azione. L'attività deve comunque essere presente nel file manifest.
e per l'esportazione.
@SearchIndexable public class LanguagesAndInputFragment extends SettingsFragment { [...] public static final CarBaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new CarBaseSearchIndexProvider(R.xml.languages_and_input_fragment, LanguagesAndInputActivity.class); }
In alcuni casi speciali, alcuni metodi di CarBaseSearchIndexProvider
potrebbe essere necessario eseguire l'override per ottenere i risultati desiderati. Ad esempio, nel
NetworkAndInternetFragment
, le preferenze relative alla rete mobile sono state
non vengano indicizzate su dispositivi senza rete mobile. In questo caso, sostituisci
getNonIndexableKeys
e contrassegna le chiavi appropriate come
non indicizzabile quando un dispositivo non dispone di una rete mobile.
@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; } }; }
A seconda delle esigenze del particolare frammento, altri metodi del
CarBaseSearchIndexProvider
può essere sostituito per includere altri
come dati indicizzabili, come dati non elaborati statici e dinamici.
@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; } }; }
Impostazioni inserite nell'indice
Per inserire un'impostazione da indicizzare:
- Definisci un
SearchIndexablesProvider
per la tua app estendendo il valoreandroid.provider.SearchIndexablesProvider
corso. - Aggiorna il valore
AndroidManifest.xml
dell'app con il fornitore nel passaggio 1. Il formato è:<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>
-
Aggiungi dati indicizzabili al tuo provider. L'implementazione dipende dalle esigenze
l'app. È possibile indicizzare due diversi tipi di dati:
SearchIndexableResource
eSearchIndexableRaw
.
Esempio di 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; } } }