Penelusuran setelan memungkinkan Anda menelusuri dan mengubah setelan setelan di aplikasi Setelan Otomotif tanpa menavigasi menu aplikasi untuk menemukannya. Penelusuran adalah cara paling efektif untuk menemukan setelan tertentu. Secara {i>default<i}, search, hanya akan menemukan setelan AOSP. Setelan tambahan, baik yang dimasukkan maupun tidak, memerlukan perubahan tambahan agar dapat diindeks.
Persyaratan
Agar setelan dapat diindeks oleh penelusuran Setelan, data harus berasal dari:
- Fragmen
SearchIndexable
di dalamCarSettings
. - Aplikasi tingkat sistem.
Menentukan data
Kolom umum:
Key
. (Wajib) Kunci String unik yang dapat dibaca manusia untuk mengidentifikasi hasil.IconResId
. Opsional Jika ikon muncul di aplikasi Anda di samping hasil, lalu tambahkan ID resource. Jika tidak, abaikan.IntentAction
. Wajib jikaIntentTargetPackage
atauIntentTargetClass
tidak ditetapkan. Menentukan tindakan yang akan diberikan hasil penelusuran maksudnya.IntentTargetPackage
. Wajib jikaIntentAction
tidak didefinisikan. Mendefinisikan paket yang akan diselesaikan oleh maksud hasil penelusuran.IntentTargetClass
. Wajib jikaIntentAction
tidak didefinisikan. Mendefinisikan class (aktivitas) yang akan diselesaikan oleh intent hasil penelusuran.
Khusus SearchIndexableResource
:
XmlResId
. (Wajib) Menentukan ID resource XML dari halaman yang berisi hasil yang akan diindeks.
Khusus SearchIndexableRaw
:
Title
. (Wajib) Judul hasil penelusuran.SummaryOn
. (Opsional) Ringkasan hasil penelusuran.Keywords
. (Opsional) Daftar kata yang terkait dengan hasil penelusuran. Mencocokkan kueri dengan hasil Anda.ScreenTitle
. (Opsional) Judul halaman dengan hasil penelusuran Anda.
Sembunyikan data
Setiap hasil penelusuran akan muncul di Penelusuran kecuali jika ditandai sebaliknya. Meskipun statis hasil penelusuran di-cache, daftar baru kunci yang tidak dapat diindeks diambil setiap kali penelusuran dibuka. Alasan menyembunyikan hasil dapat mencakup:
- Duplikat. Misalnya, muncul di beberapa halaman.
- Hanya ditampilkan dengan syarat. Misalnya, hanya menampilkan setelan data seluler jika kartu SIM ada).
- Halaman template. Misalnya, halaman detail untuk setiap aplikasi.
- Setelan memerlukan lebih banyak konteks daripada Judul dan Subtitel. Sebagai misalnya, "Setelan" yang hanya relevan dengan judul layar.
Untuk menyembunyikan setelan, penyedia atau SEARCH_INDEX_DATA_PROVIDER
harus
menampilkan kunci hasil penelusuran dari getNonIndexableKeys
. Kunci tersebut dapat
selalu ditampilkan (duplikat, kasus halaman bertemplate) atau ditambahkan bersyarat (tidak ada versi seluler
{i>data case<i}).
Indeks statis
Gunakan indeks statis jika data indeks Anda selalu sama. Misalnya, judul dan ringkasan data XML atau data mentah {i>hard code<i}. Data statis diindeks hanya sekali ketika penelusuran Setelan pertama kali diluncurkan.
Untuk file yang dapat diindeks di dalam setelan, implementasikan getXmlResourcesToIndex
dan/atau getRawDataToIndex
. Untuk setelan yang dimasukkan, implementasikan metode
Metode queryXmlResources
dan/atau queryRawData
.
Indeks dinamis
Jika data yang dapat diindeks dapat diperbarui, gunakan metode dinamis untuk mengindeks data Anda. Penelusuran setelan akan memperbarui daftar dinamis ini saat diluncurkan.
Untuk file yang dapat diindeks di dalam setelan, implementasikan getDynamicRawDataToIndex
.
Untuk setelan yang dimasukkan, implementasikan queryDynamicRawData methods
.
Indeks di Setelan Mobil
Untuk mengindeks berbagai setelan agar disertakan dalam fitur penelusuran, lihat
Konten
penyedia layanan. SettingsLib
dan android.provider
paket sudah memiliki antarmuka yang ditentukan
dan kelas abstrak untuk diperluas
menyediakan entri baru untuk diindeks. Dalam setelan AOSP, implementasi
digunakan untuk mengindeks hasil. Antarmuka utama yang harus dipenuhi adalah
SearchIndexablesProvider
, yang digunakan oleh
SettingsIntelligence
untuk mengindeks data.
public abstract class SearchIndexablesProvider extends ContentProvider { public abstract Cursor queryXmlResources(String[] projection); public abstract Cursor queryRawData(String[] projection); public abstract Cursor queryNonIndexableKeys(String[] projection); }
Secara teori, setiap fragmen dapat ditambahkan ke hasil dengan
SearchIndexablesProvider
, dalam hal ini SettingsIntelligence
merasa puas. Agar proses penambahan fragmen mudah dikelola,
gunakan pembuatan kode SettingsLib
dari SearchIndexableResources
.
Khusus untuk Setelan Mobil, setiap fragmen yang dapat diindeks dianotasi dengan
@SearchIndexable
dan memiliki SearchIndexProvider
statis
yang menyediakan data yang relevan untuk fragmen tersebut. Fragmen dengan
anotasi, tetapi tidak memiliki hasil SearchIndexProvider
dalam kompilasi
{i>error<i}.
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); }
Semua fragmen ini kemudian ditambahkan ke fragmen
Class SearchIndexableResourcesAuto
, yang merupakan wrapper tipis
di sekitar daftar kolom SearchIndexProvider
untuk semua fragmen.
Dukungan diberikan untuk menentukan target spesifik untuk anotasi (seperti
Auto, TV, dan Wear), tetapi sebagian besar anotasi tetap menggunakan setelan default (All
).
Dalam kasus penggunaan ini, tidak ada kebutuhan khusus untuk menentukan target Otomatis, sehingga tetap
sebagai All
. Implementasi dasar dari SearchIndexProvider
dimaksudkan agar memadai untuk sebagian besar fragmen.
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; } }
Mengindeks fragmen baru
Dengan desain ini, relatif mudah untuk menambahkan SettingsFragment
baru
agar diindeks, biasanya update dua baris yang menyediakan XML untuk fragmen dan
intent yang harus diikuti. Dengan WifiSettingsFragment
sebagai contoh:
@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); }
Implementasi AAOS dari SearchIndexablesProvider
, yang
menggunakan SearchIndexableResources
dan melakukan terjemahan dari
SearchIndexProviders
ke dalam skema database untuk
SettingsIntelligence
, tetapi tidak bergantung pada fragmen
diindeks. SettingsIntelligence
mendukung berapa pun
penyedia layanan tertentu, sehingga penyedia baru dapat dibuat
untuk mendukung penggunaan khusus
kasus, yang memungkinkan tiap-tiap kasus menjadi terspesialisasi dan berfokus pada hasil dengan
Google. Preferensi yang ditentukan dalam PreferenceScreen
setiap fragmen harus memiliki kunci unik
yang ditetapkan padanya agar
terindeks. Selain itu, PreferenceScreen
harus memiliki kunci
agar judul layar dapat diindeks.
Contoh indeks
Dalam beberapa kasus, fragmen mungkin tidak memiliki tindakan intent tertentu yang terkait
dengannya. Dalam kasus seperti itu, Anda bisa meneruskan class aktivitas ke dalam
Intent CarBaseSearchIndexProvider
menggunakan komponen, bukan
suatu tindakan. Ini masih memerlukan aktivitas ada dalam file manifes
dan dapat diekspor.
@SearchIndexable public class LanguagesAndInputFragment extends SettingsFragment { [...] public static final CarBaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new CarBaseSearchIndexProvider(R.xml.languages_and_input_fragment, LanguagesAndInputActivity.class); }
Dalam beberapa kasus khusus, beberapa metode CarBaseSearchIndexProvider
mungkin perlu diganti agar hasil yang diinginkan dapat diindeks. Misalnya, di
NetworkAndInternetFragment
, preferensi yang terkait dengan jaringan seluler sebelumnya
tidak diindeks pada perangkat
tanpa jaringan seluler. Dalam hal ini, ganti
getNonIndexableKeys
dan tandai kunci yang sesuai sebagai
tidak dapat diindeks jika perangkat tidak memiliki jaringan seluler.
@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; } }; }
Tergantung pada kebutuhan fragmen tertentu, metode lain dari
CarBaseSearchIndexProvider
mungkin diganti untuk menyertakan
data yang dapat diindeks, seperti data mentah statis dan dinamis.
@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; } }; }
Setelan indeks yang dimasukkan
Untuk memasukkan setelan yang akan diindeks:
- Tentukan
SearchIndexablesProvider
untuk aplikasi Anda dengan memperluas Classandroid.provider.SearchIndexablesProvider
. - Update
AndroidManifest.xml
aplikasi dengan penyedia di Langkah 1. Formatnya adalah:<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>
-
Tambahkan data yang dapat diindeks ke penyedia Anda. Implementasinya tergantung pada kebutuhan
aplikasi. Dua jenis data yang berbeda dapat diindeks:
SearchIndexableResource
danSearchIndexableRaw
.
Contoh 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; } } }