Indeksowanie wyszukiwania w Ustawieniach samochodu

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 elementu CarSettings.
  • 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śli IntentTargetPackage lub Element IntentTargetClass 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:

  1. Określ SearchIndexablesProvider dla swojej aplikacji, rozszerzając android.provider.SearchIndexablesProvider zajęcia.
  2. 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>
    
  3. 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 i SearchIndexableRaw.

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;
        }
    }
}