Araç Ayarları arama indeksleme

Ayarlar arama, Otomotiv Ayarları uygulamasındaki belirli ayarları bulmak için uygulama menülerinde gezinmeye gerek kalmadan hızlı ve kolay bir şekilde aramanıza ve değiştirmenize olanak tanır. Arama, belirli bir ayarı bulmanın en etkili yoludur. Varsayılan olarak arama yalnızca AOSP ayarlarını bulur. Ek ayarların, enjekte edilmiş olsun ya da olmasın, indekslenmesi için ek değişiklikler yapılması gerekir.

Gereksinimler

Bir ayarın Ayarlar aramasıyla dizine eklenebilir olması için verilerin aşağıdakilerden gelmesi gerekir:

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

Verileri tanımlayın

Ortak alanlar:

  • Key . ( Gerekli ) Sonucu tanımlamak için insanlar tarafından okunabilen benzersiz Dize anahtarı.
  • IconResId . İsteğe Bağlı Uygulamanızda sonucun yanında bir simge görünürse kaynak kimliğini ekleyin, aksi halde dikkate almayın.
  • IntentAction . IntentTargetPackage veya IntentTargetClass tanımlanmamışsa gereklidir . Arama sonucu amacının gerçekleştirmesi gereken eylemi tanımlar.
  • IntentTargetPackage . IntentAction tanımlanmamışsa gereklidir . Arama sonucu amacının çözümleneceği paketi tanımlar.
  • IntentTargetClass . IntentAction tanımlanmamışsa gereklidir . Arama sonucu amacının çözümleneceği sınıfı (etkinliği) tanımlar.

Yalnızca SearchIndexableResource :

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

Yalnızca SearchIndexableRaw :

  • Title . ( Gerekli ) 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 gizle

Aksi belirtilmediği sürece her arama sonucu Arama'da görünür. Statik arama sonuçları önbelleğe alınırken, arama her açıldığında indekslenemeyen anahtarların yeni bir listesi alınır. Sonuçların gizlenmesinin nedenleri şunları içerebilir:

  • Kopyalamak. Örneğin, birden fazla sayfada görünüyor.
  • Yalnızca koşullu olarak gösterilir. Örneğin, yalnızca SIM kart mevcut olduğunda mobil veri ayarlarını gösterir).
  • Şablonlu sayfa. Örneğin, bireysel bir uygulamanın ayrıntılar sayfası.
  • Ayar, Başlık ve Alt Başlıktan daha fazla bağlama ihtiyaç duyar. Örneğin, yalnızca ekran başlığıyla ilgili olan "Ayarlar" ayarı.

Bir ayarı gizlemek için sağlayıcınız veya SEARCH_INDEX_DATA_PROVIDER , arama sonucunun anahtarını getNonIndexableKeys döndürmelidir. Anahtar her zaman döndürülebilir (yinelenen, şablonlu sayfa durumları) veya koşullu olarak eklenebilir (mobil veri durumu yok).

Statik indeks

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

Ayarların içindeki indekslenebilir öğeler için getXmlResourcesToIndex ve/veya getRawDataToIndex uygulayın. Eklenen ayarlar için queryXmlResources ve/veya queryRawData yöntemlerini uygulayın.

Dinamik dizin

Dizine eklenebilir veriler uygun şekilde güncellenebiliyorsa verilerinizi dizine eklemek için dinamik yöntemi kullanın. Ayarlar araması, başlatıldığında bu dinamik listeyi günceller.

Ayarların içindeki indekslenebilir öğeler için getDynamicRawDataToIndex uygulayın. Eklenen ayarlar için queryDynamicRawData methods uygulayın.

Araç Ayarlarında Dizin

Arama özelliğine dahil edilecek farklı ayarları dizine eklemek için bkz. İçerik sağlayıcılar . SettingsLib ve android.provider paketleri, indekslenecek yeni girişlerin sağlanması için genişletilecek tanımlı arayüzlere ve soyut sınıflara zaten sahiptir. AOSP ayarlarında, bu sınıfların uygulamaları sonuçları indekslemek için kullanılır. Gerçekleştirilecek birincil arayüz, SettingsIntelligence tarafından verileri indekslemek için kullanılan SearchIndexablesProvider .

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 parça SearchIndexablesProvider sonuçlara eklenebilir; bu durumda SettingsIntelligence içerik olacaktır. Yeni parçalar eklemek ve işlemin bakımını kolaylaştırmak için SearchIndexableResources SettingsLib kod oluşturma özelliğini kullanın. Araç Ayarlarına özel olarak, her indekslenebilir parçaya @SearchIndexable ile açıklama eklenir ve ardından bu parça için ilgili verileri sağlayan statik bir SearchIndexProvider alanı bulunur. Ek açıklamaya sahip ancak SearchIndexProvider eksik olduğu parçalar derleme hatasıyla sonuçlanır.

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

Bu parçaların tümü daha sonra, tüm parçalar için SearchIndexProvider alanları listesinin etrafındaki ince bir sarmalayıcı olan otomatik olarak oluşturulan SearchIndexableResourcesAuto sınıfına eklenir. Bir ek açıklama için (Otomatik, TV ve Aşınma gibi) belirli bir hedefin belirlenmesine yönelik destek sağlanır, ancak çoğu ek açıklama varsayılanda ( All ) bırakılır. Bu kullanım durumunda Otomatik hedefin belirtilmesine ö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 parçayı indeksle

Bu tasarımla, dizine eklenecek yeni bir SettingsFragment eklemek nispeten kolaydır; genellikle parça için XML ve takip edilecek amaç sağlayan iki satırlı bir güncelleme. Örnek olarak WifiSettingsFragment ile:

@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 , SearchIndexableResources kullanan ve SearchIndexProviders SettingsIntelligence için veritabanı şemasına çeviri yapan, ancak hangi parçaların dizine eklendiğinden bağımsız olan AAOS uygulaması. SettingsIntelligence herhangi bir sayıda sağlayıcıyı destekler; böylece özel kullanım durumlarını desteklemek için yeni sağlayıcılar oluşturulabilir ve her birinin benzer yapılarla uzmanlaşmasına ve sonuçlara odaklanmasına olanak sağlanır. Fragman için PreferenceScreen tanımlanan tercihlerin her birinin indekslenebilmesi için kendilerine atanmış benzersiz bir anahtara sahip olması gerekir. Ek olarak, ekran başlığının dizine eklenmesi için PreferenceScreen atanmış bir anahtarın olması gerekir.

Dizin örnekleri

Bazı durumlarda, bir parçanın kendisiyle ilişkilendirilmiş belirli bir niyet eylemi olmayabilir. Bu gibi durumlarda, bir eylem yerine bir bileşen kullanarak aktivite sınıfını CarBaseSearchIndexProvider amacına geçirmek mümkündür. Bu yine de etkinliğin bildirim dosyasında mevcut olmasını ve dışa aktarılmasını gerektirir.

@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, indekslenecek istenen sonuçları elde etmek için CarBaseSearchIndexProvider bazı yöntemlerinin geçersiz kılınması gerekebilir. Örneğin, NetworkAndInternetFragment , mobil ağ ile ilgili tercihler, mobil ağı olmayan cihazlarda dizine eklenmeyecekti. Bu durumda, getNonIndexableKeys yöntemini geçersiz kılın ve bir cihazın mobil ağı olmadığında uygun anahtarları indekslenemez 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;
                }
            };
}

Belirli bir parçanın ihtiyaçlarına bağlı olarak, CarBaseSearchIndexProvider diğer yöntemleri, statik ve dinamik ham veriler gibi diğer indekslenebilir 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;
                }
            };
}

Dizin enjekte edilen ayarlar

Dizine eklenecek bir ayarı enjekte etmek için:

  1. android.provider.SearchIndexablesProvider sınıfını genişleterek uygulamanız için bir SearchIndexablesProvider tanımlayın.
  2. 1. Adımda uygulamanın AndroidManifest.xml dosyasını sağlayıcıyla güncelleyin. Format ş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 indekslenebilir veriler ekleyin. Uygulama, uygulamanın ihtiyaçlarına bağlıdır. İki 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;
        }
    }
}