Mit der Suchfunktion in den Einstellungen können Sie schnell und einfach nach bestimmten Einstellungen in der App „Einstellungen für die Mobilität“ suchen und sie ändern, ohne durch die App-Menüs gehen zu müssen. Die Suche ist die effektivste Methode, um eine bestimmte Einstellung zu finden. Standardmäßig werden bei der Suche nur AOSP-Einstellungen gefunden. Zusätzliche Einstellungen, unabhängig davon, ob sie eingefügt wurden oder nicht, erfordern zusätzliche Änderungen, um indexiert zu werden.
Voraussetzungen
Damit eine Einstellung in der Einstellungssuche indexiert werden kann, müssen die Daten aus einer der folgenden Quellen stammen:
SearchIndexable
-Fragment inCarSettings
.- App auf Systemebene.
Daten definieren
Gängige Felder:
Key
(Erforderlich) Eindeutiger menschenlesbarer Stringschlüssel zur Identifizierung des Ergebnisses.IconResId
Optional: Wenn in Ihrer App neben dem Symbol und fügen Sie dann die Ressourcen-ID hinzu. Andernfalls ignorieren Sie die Anfrage.IntentAction
Erforderlich, wennIntentTargetPackage
oderIntentTargetClass
ist nicht definiert. Definiert die Aktion, die mit dem Suchergebnis ausgeführt werden soll.IntentTargetPackage
: Erforderlich, wennIntentAction
nicht definiert ist. Definiert das Paket, auf das die Suchergebnisabsicht verweisen soll.IntentTargetClass
: Erforderlich, wennIntentAction
nicht definiert ist. Definiert die Klasse (Aktivität), auf die die Suchergebnisabsicht aufgelöst werden soll.
Nur SearchIndexableResource
:
XmlResId
: (Erforderlich) Definiert die XML-Ressourcen-ID der Seite mit den zu indexierenden Ergebnissen.
Nur SearchIndexableRaw
:
Title
: (Erforderlich) Titel des Suchergebnisses.SummaryOn
: (Optional) Zusammenfassung des Suchergebnisses.Keywords
: (Optional) Liste der Wörter, die mit dem Suchergebnis verknüpft sind. Ordnet die Abfrage Ihrem Ergebnis zu.ScreenTitle
: (Optional) Titel der Seite mit dem Suchergebnis.
Daten ausblenden
Jedes Suchergebnis wird in der Suche angezeigt, sofern es nicht anders gekennzeichnet ist. Während statische Suchergebnisse im Cache gespeichert werden, wird jedes Mal, wenn die Suche geöffnet wird, eine neue Liste nicht indexierbarer Schlüssel abgerufen. Mögliche Gründe für das Ausblenden von Ergebnissen:
- Duplikat. zum Beispiel auf mehreren Seiten.
- Wird nur bedingt angezeigt. Zeigt beispielsweise nur Einstellungen für mobile Daten an wenn eine SIM-Karte vorhanden ist).
- Vorlagenseite: Beispiel: Detailseite einer einzelnen App.
- Die Umgebung erfordert mehr Kontext als ein Titel und Untertitel. Beispiel: Eine Einstellung „Einstellungen“, die nur für den Bildschirmtitel relevant ist.
Zum Ausblenden einer Einstellung muss dein Anbieter oder SEARCH_INDEX_DATA_PROVIDER
Folgendes tun:
gibt den Schlüssel des Suchergebnisses von getNonIndexableKeys
zurück. Der Schlüssel kann immer zurückgegeben (Duplikate, Seiten mit Vorlagen) oder bedingt hinzugefügt werden (kein Fall mit mobilen Daten).
Statischer Index
Verwenden Sie den statischen Index, wenn Ihre Indexdaten immer gleich sind. Beispielsweise der Titel und die Zusammenfassung der XML-Daten oder die Rohdaten des Hardcodes. Die statischen Daten werden nur einmal indexiert, wenn die Suche in den Einstellungen zum ersten Mal gestartet wird.
Für indexierbare Elemente in den Einstellungen implementieren Sie getXmlResourcesToIndex
und/oder getRawDataToIndex
. Implementieren Sie für injizierte Einstellungen den
queryXmlResources
- und/oder queryRawData
-Methoden.
Dynamischer Index
Wenn die indexierbaren Daten entsprechend aktualisiert werden können, verwenden Sie die dynamische Methode, Ihre Daten zu indexieren. Die Einstellungssuche aktualisiert diese dynamische Liste, wenn sie aktiviert wird.
Implementieren Sie getDynamicRawDataToIndex
für indexierte Inhalte in den Einstellungen.
Implementiere für die eingefügten Einstellungen die queryDynamicRawData methods
.
In den Autoeinstellungen indexieren
Informationen zum Indexieren verschiedener Einstellungen, die in die Suchfunktion einbezogen werden sollen, finden Sie unter Inhaltsanbieter. SettingsLib
und android.provider
Pakete haben bereits definierte Schnittstellen und abstrakte Klassen, die erweitert werden sollen
neue Einträge für die Indexierung bereitgestellt. In den AOSP-Einstellungen werden Implementierungen dieser Klassen zum Indexieren von Ergebnissen verwendet. Die primäre Schnittstelle, die erfüllt werden muss, ist SearchIndexablesProvider
. Sie wird von SettingsIntelligence
zum Indexieren von Daten verwendet.
public abstract class SearchIndexablesProvider extends ContentProvider { public abstract Cursor queryXmlResources(String[] projection); public abstract Cursor queryRawData(String[] projection); public abstract Cursor queryNonIndexableKeys(String[] projection); }
Theoretisch könnte jedes Fragment den Ergebnissen in SearchIndexablesProvider
hinzugefügt werden. In diesem Fall wäre SettingsIntelligence
ein Inhalt. Verwenden Sie die SettingsLib
-Codegenerierung von SearchIndexableResources
, um den Prozess möglichst einfach zu gestalten und neue Fragmente hinzuzufügen.
Speziell für die Autoeinstellungen ist jedes indizierbare Fragment mit
@SearchIndexable
und hat dann einen statischen SearchIndexProvider
das die relevanten Daten für dieses Fragment enthält. Fragmente mit dem
Annotation, aber ohne SearchIndexProvider
führen zu einer Kompilierung
Fehler.
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); }
Alle diese Fragmente werden dann der automatisch generierten SearchIndexableResourcesAuto
-Klasse hinzugefügt, die eine dünne Ummantelung um die Liste der SearchIndexProvider
-Felder für alle Fragmente ist.
Es wird unterstützt, ein bestimmtes Ziel für eine Annotation anzugeben (z. B.
Auto, TV und Wear). Für die meisten Anmerkungen wird jedoch die Standardeinstellung beibehalten (All
).
In diesem Anwendungsfall muss das automatische Ziel nicht speziell angegeben werden. Es bleibt also
als All
. Die Basisimplementierung von SearchIndexProvider
für die meisten Fragmente ausreichen.
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; } }
Neues Fragment indexieren
Mit diesem Design ist es relativ einfach, ein neues zu indexierendes SettingsFragment
hinzuzufügen. In der Regel ist dafür eine Aktualisierung mit zwei Zeilen erforderlich, die die XML-Datei für das Fragment und die befolgte Absicht enthält. Beispiel für 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); }
Die AAOS-Implementierung von SearchIndexablesProvider
, die
verwendet SearchIndexableResources
und führt die Übersetzung
SearchIndexProviders
in das Datenbankschema für
SettingsIntelligence
, ist aber unabhängig von den Fragmenten.
bei der Indexierung. SettingsIntelligence
unterstützt beliebig viele
damit neue Anbieter erstellt werden können,
So kann sich jeder spezialisieren und sich auf Ergebnisse mit ähnlichen
Strukturen. Die in PreferenceScreen
definierten Einstellungen
für das Fragment muss jeweils ein eindeutiger Schlüssel zugewiesen sein, damit
indexiert. Außerdem muss PreferenceScreen
einen Schlüssel haben
, die dem Bildschirmtitel zugewiesen sind, der indexiert werden soll.
Indexbeispiele
In einigen Fällen ist einem Fragment keine bestimmte Intent-Aktion zugeordnet
damit verbunden. In solchen Fällen können Sie die Aktivitätsklasse über eine Komponente anstelle einer Aktion an den CarBaseSearchIndexProvider
-Intent übergeben. Dazu muss die Aktivität jedoch in der Manifestdatei vorhanden und exportiert werden.
@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 einigen Sonderfällen müssen einige Methoden von CarBaseSearchIndexProvider
möglicherweise überschrieben werden, damit die gewünschten Ergebnisse indexiert werden. Beispielsweise sollten in NetworkAndInternetFragment
Einstellungen für Mobilfunknetze nicht auf Geräten ohne Mobilfunknetz indexiert werden. Überschreiben Sie in diesem Fall die getNonIndexableKeys
-Methode und kennzeichnen Sie die entsprechenden Schlüssel als nicht indexierbar, wenn ein Gerät kein Mobilfunknetz hat.
@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; } }; }
Je nach den Anforderungen des jeweiligen Fragments können andere Methoden der
CarBaseSearchIndexProvider
kann überschrieben werden, um andere
Indexierbare Daten wie statische und dynamische Rohdaten.
@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; } }; }
Einstellungen für Indexeinträge
So fügen Sie eine Einstellung ein, die indexiert werden soll:
- Definieren Sie eine
SearchIndexablesProvider
für Ihre App, indem Sie die Klasseandroid.provider.SearchIndexablesProvider
erweitern. - Aktualisieren Sie die
AndroidManifest.xml
der App bei dem Anbieter in Schritt 1. Das Format ist:<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>
-
Fügen Sie Ihrem Anbieter indexierbare Daten hinzu. Die Implementierung hängt von den Anforderungen der App ab. Es können zwei verschiedene Datentypen indexiert werden:
SearchIndexableResource
undSearchIndexableRaw
.
Beispiel für 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; } } }