Wyszukiwanie ustawień pozwala szybko i łatwo znaleźć i zmienić określone ustawienia ustawieniach w sekcji Automotive Settings (Ustawienia samochodu) bez konieczności przechodzenia do menu ją znaleźć. Najskuteczniejszym sposobem na znalezienie konkretnego ustawienia jest wyszukiwanie. Domyślnie znajduje tylko ustawienia AOSP. Dodatkowe ustawienia, niezależnie od tego, czy są wstrzykiwane, czy nie wymagają dodatkowych zmian w celu zindeksowania.
Wymagania
Aby ustawienie można było indeksować przez wyszukiwanie ustawień, dane muszą pochodzić z:
- Fragment
SearchIndexable
w obrębie elementuCarSettings
. - Aplikacja na poziomie systemu.
Zdefiniuj dane
Typowe pola:
Key
(Wymagany) Unikalny, zrozumiały dla człowieka klucz ciągu tekstowego do identyfikacji wyniku.IconResId
Opcjonalnie Jeśli w aplikacji obok ikony wynik, a następnie dodaj identyfikator zasobu. W przeciwnym razie zignoruj.IntentAction
Wymagany, jeśliIntentTargetPackage
lub ElementIntentTargetClass
nie został zdefiniowany. Definiuje działanie wykonywane w wyniku wyszukiwania podejmujemy działania.IntentTargetPackage
Wymagany, jeśli wartośćIntentAction
nie jest wartością zdefiniowano jego definicję. Definiuje pakiet, z którym mają być kierowane wyniki wyszukiwania.IntentTargetClass
Wymagany, jeśli wartośćIntentAction
nie jest wartością zdefiniowano jego definicję. Definiuje klasę (aktywność), do której ma przejść intencja wyniku wyszukiwania.
Tylko SearchIndexableResource
:
XmlResId
(Wymagane) Określa identyfikator zasobu XML strony zawierającej wyniki do zindeksowania.
Tylko SearchIndexableRaw
:
Title
(Wymagany) Tytuł wyniku wyszukiwania.SummaryOn
(Opcjonalnie) Podsumowanie wyniku wyszukiwania.Keywords
(Opcjonalnie) Lista słów powiązanych z wynikiem wyszukiwania. Dopasowuje zapytanie do wyniku.ScreenTitle
(Opcjonalnie) Tytuł strony z wynikiem wyszukiwania.
Ukryj dane
Każdy wynik wyszukiwania pojawia się w wyszukiwarce, chyba że zaznaczono inaczej. Na etapie statycznym wyniki wyszukiwania są zapisywane w pamięci podręcznej, za każdym razem pobierana jest nowa lista kluczy niemożliwych do zindeksowania wyszukiwanie jest otwarte. Możliwe przyczyny ukrywania wyników:
- Duplikat. na przykład na kilku stronach,
- Wyświetlane tylko warunkowo. Na przykład pokazuje tylko ustawienia mobilnej transmisji danych gdy dostępna jest karta SIM).
- Strona szablonowa. Może to być np. strona z informacjami o konkretnej aplikacji.
- Ustawienie wymaga więcej kontekstu niż tytułu i napisów. Dla: np. „Ustawienia”, , które ma zastosowanie tylko do tytułu ekranu.
Aby ukryć ustawienie, dostawca lub SEARCH_INDEX_DATA_PROVIDER
powinni:
zwraca klucz wyniku wyszukiwania z getNonIndexableKeys
. Klucz może
zawsze zwracana (zduplikowane, szablonowe przypadki stron) lub dodawane warunkowo (nie dla komórek
przypadku danych).
Indeks statyczny
Użyj indeksu statycznego, jeśli dane indeksu są zawsze takie same. Na przykład tytuł oraz podsumowania danych w formacie XML lub nieprzetworzonych danych na stałe. Dane statyczne zostaną zindeksowane. tylko raz po uruchomieniu wyszukiwania ustawień.
W przypadku elementów możliwych do zindeksowania w ustawieniach zaimplementuj getXmlResourcesToIndex
lub getRawDataToIndex
. Do wstrzykiwanych ustawień zaimplementuj funkcję
Metody queryXmlResources
lub queryRawData
.
Indeks dynamiczny
Jeśli dane możliwe do zindeksowania można odpowiednio zaktualizować, użyj metody dynamicznej do indeksowanie Twoich danych. Wyszukiwarka ustawień będzie aktualizować tę dynamiczną listę po jej uruchomieniu.
W przypadku elementów możliwych do zindeksowania w ustawieniach zaimplementuj getDynamicRawDataToIndex
.
Do wstrzykiwanych ustawień zaimplementuj queryDynamicRawData methods
.
Indeks w ustawieniach samochodu
Aby zindeksować różne ustawienia w celu uwzględniania ich w funkcji wyszukiwania, zapoznaj się z sekcją
Treść
dostawców usług. SettingsLib
i android.provider
pakiety mają już zdefiniowane interfejsy i klasy abstrakcyjne, które można rozszerzyć
przez dostarczanie nowych wpisów do indeksowania. W ustawieniach AOSP implementacje tych
są używane do indeksowania wyników. Główny interfejs do realizacji to
SearchIndexablesProvider
, który jest używany przez
SettingsIntelligence
, aby zindeksować dane.
public abstract class SearchIndexablesProvider extends ContentProvider { public abstract Cursor queryXmlResources(String[] projection); public abstract Cursor queryRawData(String[] projection); public abstract Cursor queryNonIndexableKeys(String[] projection); }
Teoretycznie każdy fragment można dodać do wyników
SearchIndexablesProvider
, w którym to przypadku SettingsIntelligence
co byłoby odpowiednie. Aby ułatwić proces dodawania nowych fragmentów,
użyj generowania kodu za pomocą SettingsLib
dla SearchIndexableResources
.
W ustawieniach samochodu, każdy fragment, który można zindeksować, jest opatrzony adnotacją
@SearchIndexable
, a następnie zawiera statyczny element typu SearchIndexProvider
, które zawiera odpowiednie dane dla tego fragmentu. Fragmenty ze znakiem
adnotacja, ale w kompilacji brakuje wyniku 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); }
Wszystkie te fragmenty są następnie dodawane do automatycznie generowanego
Klasa SearchIndexableResourcesAuto
, która jest cienkim otoczeniem
wokół listy SearchIndexProvider
pól dla wszystkich fragmentów.
Zapewniamy wsparcie w zakresie określania konkretnego miejsca docelowego adnotacji (np.
Auto, TV i Wear), ale większość adnotacji pozostawia domyślną (All
).
W tym przypadku nie ma potrzeby określania automatycznego celu, więc pozostaje on
jako All
. Podstawowa implementacja interfejsu SearchIndexProvider
jest wystarczające w przypadku większości fragmentów.
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; } }
Indeksowanie nowego fragmentu
W przypadku tego układu łatwo jest dodać nowy SettingsFragment
do zindeksowania, zwykle dwuwierszową aktualizację zawierającą kod XML dla fragmentu i tag
który chcesz śledzić. Za pomocą przykładu 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); }
Implementacja interfejsu SearchIndexablesProvider
w systemie AAOS,
używa języka SearchIndexableResources
i wykonuje tłumaczenie z języka:
SearchIndexProviders
do schematu bazy danych dla
SettingsIntelligence
, ale nie zależy od tego, czym są fragmenty
nie są indeksowane. SettingsIntelligence
obsługuje dowolną liczbę
dostawców usług specjalistycznych, co pozwala tworzyć nowych dostawców
dzięki czemu każda z nich będzie wyspecjalizowana i koncentrowała się na wynikach
w różnych strukturach. Ustawienia określone w dokumencie PreferenceScreen
dla danego fragmentu musi mieć przypisany unikalny klucz,
. Dodatkowo PreferenceScreen
musi mieć klucz
do indeksu.
Przykłady indeksu
W niektórych przypadkach z fragmentem może nie być powiązane konkretne działanie intencji
z nim. W takich przypadkach można przekazać klasę aktywności do funkcji
intencja CarBaseSearchIndexProvider
używa komponentu, a nie komponentu.
działanie. Mimo to aktywność musi znajdować się w pliku manifestu
i eksportowania.
@SearchIndexable public class LanguagesAndInputFragment extends SettingsFragment { [...] public static final CarBaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new CarBaseSearchIndexProvider(R.xml.languages_and_input_fragment, LanguagesAndInputActivity.class); }
W pewnych szczególnych przypadkach niektóre metody CarBaseSearchIndexProvider
może wymagać zastąpienia, aby wyniki zostały zindeksowane. Na przykład w polu
NetworkAndInternetFragment
, preferencje dotyczące sieci komórkowej były
aby nie były indeksowane na urządzeniach bez sieci komórkowej. W takim przypadku zastąp
getNonIndexableKeys
i oznacz odpowiednie klucze jako
nie można zindeksować, gdy urządzenie nie jest połączone z siecią komórkową.
@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; } }; }
W zależności od potrzeb danego fragmentu, inne metody
Pole CarBaseSearchIndexProvider
może zostać zastąpione, aby uwzględnić inne
danych, które można indeksować, takich jak statyczne i dynamiczne dane nieprzetworzone.
@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; } }; }
Ustawienia wstrzykiwanego indeksu
Aby wprowadzić ustawienie do zindeksowania:
- Określ
SearchIndexablesProvider
dla swojej aplikacji, rozszerzającandroid.provider.SearchIndexablesProvider
zajęcia. - Zaktualizuj uprawnienie
AndroidManifest.xml
aplikacji, podając dostawcę w kroku 1. Format to:<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>
-
Dodaj do dostawcy dane, które można zindeksować. Wdrożenie zależy od potrzeb
aplikację. Można indeksować 2 różne typy danych:
SearchIndexableResource
iSearchIndexableRaw
.
Przykład 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; } } }