Araç Ayarları arama dizine ekleme

Ayarlar araması, uygulama menülerine gitmeden Automotive Ayarları uygulamasında belirli ayarları hızlı ve kolay bir şekilde aramanızı ve değiştirmenizi sağlar. Arama, belirli bir ayarı bulmanın en etkili yoludur. Arama, varsayılan olarak yalnızca AOSP ayarlarını bulur. Ek ayarların, eklenmiş veya eklenmemiş olması fark etmeksizin dizine eklenmesi için ek değişiklikler yapılması gerekir.

Şartlar

Bir ayarın Ayarlar araması tarafından dizine eklenebilir olması için verilerin şu kaynaklardan gelmesi gerekir:

  • CarSettings içindeki SearchIndexable parçası.
  • Sistem düzeyinde uygulama.

Verileri tanımlama

Yaygın alanlar:

  • Key. (Zorunlu) Sonucu tanımlamak için kullanıcı tarafından okunabilen benzersiz dize anahtarı.
  • IconResId. İsteğe bağlı Uygulamanızda sonucun yanında bir simge görünüyorsa kaynak kimliğini ekleyin, aksi takdirde yoksayın.
  • IntentAction. IntentTargetPackage veya IntentTargetClass tanımlanmamışsa zorunludur. Arama sonucunun amacının gerçekleştireceği işlemi tanımlar.
  • IntentTargetPackage. IntentAction tanımlanmamışsa zorunludur. Arama sonucu amacının çözüme ulaştıracağı paketi tanımlar.
  • IntentTargetClass. IntentAction tanımlanmamışsa zorunludur. Arama sonucu amacının çözeceği sınıfı (etkinliği) tanımlar.

Yalnızca SearchIndexableResource:

  • XmlResId. (Zorunlu) Dizine eklenecek sonuçları içeren sayfanın XML kaynak kimliğini tanımlar.

Yalnızca SearchIndexableRaw:

  • Title. (Zorunlu) Arama sonucunun başlığı.
  • SummaryOn. (İsteğe bağlı) Arama sonucunun özeti.
  • Keywords. (İsteğe bağlı) Arama sonucuyla ilişkili kelimelerin listesi. Sorguyu sonucunuzla eşleştirir.
  • ScreenTitle. (İsteğe bağlı) Arama sonucunuzun bulunduğu sayfanın başlığı.

Verileri gizleme

Aksi belirtilmediği sürece her arama sonucu Arama'da gösterilir. Statik arama sonuçları önbelleğe alınırken, arama her açıldığında dizine eklenemeyen anahtarların yeni bir listesi alınır. Sonuçların gizlenmesine neden olabilecek durumlar şunlardır:

  • Kopyala. Örneğin, birden fazla sayfada görünür.
  • Yalnızca koşullu olarak gösterilir. Örneğin, yalnızca SIM kart varsa mobil veri ayarlarını gösterir).
  • Şablon sayfası. Örneğin, tek bir uygulamanın ayrıntılar sayfası.
  • Ayarın, başlık ve altyazıdan daha fazla bağlama ihtiyacı vardır. Örneğin, yalnızca ekran başlığıyla alakalı olan bir "Ayarlar" ayarı.

Bir ayarı gizlemek için sağlayıcınız veya SEARCH_INDEX_DATA_PROVIDER, getNonIndexableKeys'ten arama sonucunun anahtarını döndürmelidir. Anahtar her zaman döndürülebilir (kopya, şablon sayfası örnekleri) veya koşullu olarak eklenebilir (mobil veri örneği yok).

Statik dizin

Dizin verileriniz her zaman aynıysa statik dizini kullanın. Örneğin, XML verilerinin başlığı ve özeti veya sabit kodlu ham veriler. Statik veriler, Ayarlar araması ilk kez başlatıldığında yalnızca bir kez dizine eklenir.

İçerideki ayarlarda dizine eklenebilirler için getXmlResourcesToIndex ve/veya getRawDataToIndex'i uygulayın. Enjekte edilen ayarlar için queryXmlResources ve/veya queryRawData yöntemlerini uygulayın.

Dinamik dizin

Dizine eklenebilir veriler uygun şekilde güncellenebilirse verilerinizi dizine eklemek için dinamik yöntemi kullanın. Ayarlar araması, kullanıma sunulduğunda bu dinamik listeyi günceller.

Ayarlar içindeki dizine eklenebilirler için getDynamicRawDataToIndex'ü uygulayın. Enjekte edilen ayarlar için queryDynamicRawData methods öğesini uygulayın.

Araç ayarlarında dizin

Arama özelliğine dahil edilecek farklı ayarları dizine eklemek için İçerik sağlayıcılar başlıklı makaleyi inceleyin. SettingsLib ve android.provider paketlerinde, dizine eklenecek yeni girişler sağlamak için genişletilecek tanımlanmış arayüzler ve soyut sınıflar zaten mevcuttur. AOSP ayarlarında, sonuçları dizine eklemek için bu sınıfların uygulamaları kullanılır. Gerçekleştirilmesi gereken birincil arayüz, SettingsIntelligence tarafından verileri dizine eklemek için kullanılan SearchIndexablesProvider arayüzüdür.

public abstract class SearchIndexablesProvider extends ContentProvider {
    public abstract Cursor queryXmlResources(String[] projection);
    public abstract Cursor queryRawData(String[] projection);
    public abstract Cursor queryNonIndexableKeys(String[] projection);
}

Teorik olarak her bir parça SearchIndexablesProvider'teki sonuçlara eklenebilir. Bu durumda SettingsIntelligence içerik olur. Yeni parçalar eklemek için sürecin kolayca sürdürülebilmesini sağlamak amacıyla SearchIndexableResources'un SettingsLib kod oluşturma özelliğini kullanın. Araç Ayarları'na özgü olarak, dizine eklenebilir her bir parça @SearchIndexable ile ek açıklamaya sahiptir ve ardından bu parçayla ilgili verileri sağlayan statik bir SearchIndexProvider alanına sahiptir. Not içeren ancak SearchIndexProvider eksik olan parçalar derleme hatasına neden olur.

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

Ardından bu parçaların tümü, tüm parçaların SearchIndexProvider alanları listesinin etrafında ince bir sarmalayıcı olan otomatik olarak oluşturulan SearchIndexableResourcesAuto sınıfına eklenir. Bir ek açıklama için belirli bir hedef belirtme (ör. Auto, TV ve Wear) desteği sağlanır ancak çoğu ek açıklama varsayılan olarak (All) bırakılır. Bu kullanım alanında Auto hedefini belirtmek için özel bir ihtiyaç yoktur, bu nedenle All olarak kalır. SearchIndexProvider temel uygulamasının çoğu parça için yeterli olması amaçlanmıştır.

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

Yeni bir snippet'i dizine ekleme

Bu tasarımla, dizine eklenecek yeni bir SettingsFragment eklemek nispeten kolaydır. Genellikle, parçanın XML'sini ve izlenecek amacı sağlayan iki satırlık bir güncellemedir. WifiSettingsFragment örneğini kullanarak:

@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 için AAOS uygulaması. SearchIndexableResources kullanır ve SearchIndexProviders'den SettingsIntelligence için veritabanı şemasına çeviri yapar ancak hangi parçaların dizine eklendiğine bakmaz. SettingsIntelligence, herhangi bir sayıda sağlayıcıyı destekler. Bu sayede, özel kullanım alanlarını desteklemek için yeni sağlayıcılar oluşturulabilir. Böylece her sağlayıcı, benzer yapılara sahip sonuçlara odaklanarak uzmanlaşabilir. PreferenceScreen bölümünde tanımlanan tercihlerin, dizine eklenebilmesi için her birine benzersiz bir anahtar atanmalıdır. Ayrıca, ekran başlığının dizine eklenmesi için PreferenceScreen öğesine bir anahtar atanmalıdır.

Dizin örnekleri

Bazı durumlarda, bir fragmanla ilişkili belirli bir intent işlemi olmayabilir. Bu tür durumlarda, etkinlik sınıfını bir işlem yerine bileşen kullanarak CarBaseSearchIndexProvider amacına iletebilirsiniz. Bunun için etkinliğin manifest dosyasında bulunması ve dışa aktarılması gerekir.

@SearchIndexable
public class LanguagesAndInputFragment extends SettingsFragment {
[...]
    public static final CarBaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
        new CarBaseSearchIndexProvider(R.xml.languages_and_input_fragment,
                LanguagesAndInputActivity.class);
}

Bazı özel durumlarda, istenen sonuçların dizine eklenmesini sağlamak için CarBaseSearchIndexProvider bazı yöntemlerinin geçersiz kılınması gerekebilir. Örneğin, NetworkAndInternetFragment sürümünde mobil ağla ilgili tercihler, mobil ağı olmayan cihazlarda dizine eklenmemiştir. Bu durumda, getNonIndexableKeys yöntemini geçersiz kılın ve bir cihazda mobil ağ olmadığında uygun anahtarları dizine eklenemez olarak işaretleyin.

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

Söz konusu parçanın ihtiyaçlarına bağlı olarak, CarBaseSearchIndexProvider sınıfının diğer yöntemleri, statik ve dinamik ham veriler gibi dizine eklenebilir diğer verileri içerecek şekilde geçersiz kılınabilir.

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

Dizine eklenen ayarlar

Dizine eklenecek bir ayar eklemek için:

  1. android.provider.SearchIndexablesProvider sınıfını genişleterek uygulamanız için bir SearchIndexablesProvider tanımlayın.
  2. Uygulamanın AndroidManifest.xml değerini 1. adımdaki sağlayıcıyla güncelleyin. Biçim şu şekildedir:
    <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. Sağlayıcınıza dizine eklenebilir veriler ekleyin. Uygulamanın ihtiyaçlarına bağlı olarak iki farklı veri türü dizine eklenebilir: SearchIndexableResource ve SearchIndexableRaw.

SearchIndexablesProvider örneği

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