Tính năng tìm kiếm trong chế độ cài đặt giúp bạn nhanh chóng và dễ dàng tìm kiếm cũng như thay đổi phần cài đặt trong ứng dụng Automotive Settings (Cài đặt Automotive mà không cần di chuyển qua các trình đơn ứng dụng để tìm thấy nó. Tìm kiếm là cách hiệu quả nhất để tìm một chế độ cài đặt cụ thể. Theo mặc định, tính năng tìm kiếm chỉ tìm các chế độ cài đặt AOSP (Dự án nguồn mở Android). Các chế độ cài đặt bổ sung, dù có được chèn hay không, đều cần có thêm thay đổi để được lập chỉ mục.
Yêu cầu
Để một chế độ cài đặt có thể được lập chỉ mục bằng tính năng Tìm kiếm trong phần Cài đặt, dữ liệu phải đến từ:
- Mảnh
SearchIndexable
bên trongCarSettings
. - Ứng dụng cấp hệ thống.
Xác định dữ liệu
Các trường phổ biến:
Key
. (Bắt buộc) Khoá Chuỗi duy nhất mà con người có thể đọc được để xác định kết quả.IconResId
. Không bắt buộc Nếu một biểu tượng xuất hiện trong ứng dụng của bạn bên cạnh kết quả, hãy thêm mã nhận dạng tài nguyên, nếu không, hãy bỏ qua.IntentAction
. Bắt buộc nếuIntentTargetPackage
hoặcIntentTargetClass
chưa được xác định. Xác định hành động mà ý định kết quả tìm kiếm sẽ thực hiện.IntentTargetPackage
. Bắt buộc nếuIntentAction
không phải là xác định. Xác định gói mà mục đích của kết quả tìm kiếm sẽ sử dụng.IntentTargetClass
. Bắt buộc nếuIntentAction
không phải là xác định. Xác định lớp (hoạt động) mà ý định của kết quả tìm kiếm sẽ được xử lý.
Chỉ dành cho SearchIndexableResource
:
XmlResId
. (Bắt buộc) Xác định mã nhận dạng tài nguyên XML của trang chứa kết quả cần lập chỉ mục.
Chỉ SearchIndexableRaw
:
Title
. (Bắt buộc) Tiêu đề của kết quả tìm kiếm.SummaryOn
. (Không bắt buộc) Tóm tắt kết quả tìm kiếm.Keywords
. (Không bắt buộc) Danh sách các từ được liên kết với kết quả tìm kiếm. So khớp truy vấn với kết quả của bạn.ScreenTitle
. (Không bắt buộc) Tiêu đề của trang có kết quả tìm kiếm của bạn.
Ẩn dữ liệu
Mỗi kết quả tìm kiếm sẽ xuất hiện trong Tìm kiếm, trừ phi kết quả đó được đánh dấu khác. Khi ở trạng thái tĩnh kết quả tìm kiếm được lưu vào bộ nhớ đệm, một danh sách mới các khoá không lập chỉ mục được truy xuất mỗi lần nút tìm kiếm đã được mở. Sau đây là một số lý do khiến kết quả bị ẩn:
- Sao chép. Ví dụ: xuất hiện trên nhiều trang.
- Chỉ hiển thị có điều kiện. Ví dụ: chỉ hiển thị các chế độ cài đặt dữ liệu di động khi có thẻ SIM).
- Trang được tạo bằng mẫu. Ví dụ: trang chi tiết cho một ứng dụng riêng lẻ.
- Chế độ cài đặt cần có nhiều bối cảnh hơn so với Tiêu đề và Phụ đề. Ví dụ: chế độ cài đặt "Cài đặt" chỉ liên quan đến tiêu đề màn hình.
Để ẩn một chế độ cài đặt, nhà cung cấp hoặc SEARCH_INDEX_DATA_PROVIDER
phải trả về khoá của kết quả tìm kiếm từ getNonIndexableKeys
. Khoá có thể
luôn được trả về (trường hợp trang trùng lặp, theo mẫu) hoặc được thêm theo điều kiện (không có thiết bị di động
trường hợp dữ liệu).
Chỉ số tĩnh
Sử dụng chỉ mục tĩnh nếu dữ liệu chỉ mục của bạn luôn giữ nguyên. Ví dụ: tiêu đề và bản tóm tắt dữ liệu XML hoặc dữ liệu thô của mã cứng. Dữ liệu tĩnh được lập chỉ mục một lần khi tìm kiếm Cài đặt được khởi chạy lần đầu tiên.
Đối với các mục có thể lập chỉ mục bên trong phần cài đặt, hãy triển khai getXmlResourcesToIndex
và/hoặc getRawDataToIndex
. Đối với các chế độ cài đặt được chèn, hãy triển khai
Phương thức queryXmlResources
và/hoặc queryRawData
.
Chỉ mục động
Nếu dữ liệu có thể lập chỉ mục có thể được cập nhật tương ứng, hãy sử dụng phương thức động để lập chỉ mục dữ liệu của bạn. Tìm kiếm trong phần Cài đặt sẽ cập nhật danh sách động này khi được khởi chạy.
Đối với các tài sản có thể lập chỉ mục bên trong phần cài đặt, hãy triển khai getDynamicRawDataToIndex
.
Đối với các chế độ cài đặt được chèn, hãy triển khai queryDynamicRawData methods
.
Chỉ mục trong phần Cài đặt ô tô
Để lập chỉ mục các chế độ cài đặt khác nhau nhằm đưa vào tính năng tìm kiếm, hãy xem
Nội dung
. Các gói SettingsLib
và android.provider
đã xác định các giao diện và lớp trừu tượng để mở rộng nhằm cung cấp các mục nhập mới cần được lập chỉ mục. Trong phần cài đặt AOSP, các phương thức triển khai của các lớp này được dùng để lập chỉ mục kết quả. Giao diện chính cần được thực hiện là
SearchIndexablesProvider
, được dùng bởi
SettingsIntelligence
để lập chỉ mục dữ liệu.
public abstract class SearchIndexablesProvider extends ContentProvider { public abstract Cursor queryXmlResources(String[] projection); public abstract Cursor queryRawData(String[] projection); public abstract Cursor queryNonIndexableKeys(String[] projection); }
Theo lý thuyết, mỗi mảnh có thể được thêm vào kết quả theo
SearchIndexablesProvider
, trong trường hợp đó là SettingsIntelligence
sẽ là nội dung. Để dễ dàng duy trì quy trình này bằng cách thêm các mảnh mới,
sử dụng tính năng tạo mã SettingsLib
của SearchIndexableResources
.
Riêng trong phần Cài đặt ô tô, mỗi mảnh có thể lập chỉ mục được chú thích bằng
@SearchIndexable
rồi có một SearchIndexProvider
tĩnh
cung cấp dữ liệu liên quan cho phân đoạn đó. Các mảnh có chú thích nhưng thiếu SearchIndexProvider
sẽ dẫn đến lỗi biên dịch.
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); }
Sau đó, tất cả các mảnh này sẽ được thêm vào lớp SearchIndexableResourcesAuto
được tạo tự động. Đây là một trình bao bọc mỏng xung quanh danh sách các trường SearchIndexProvider
cho tất cả các mảnh.
Hỗ trợ được cung cấp để chỉ định mục tiêu cụ thể cho chú thích (chẳng hạn như
Auto, TV và Wear), tuy nhiên, hầu hết các chú thích vẫn ở mặc định (All
).
Trong trường hợp sử dụng này, không có nhu cầu cụ thể nào để chỉ định Mục tiêu tự động, nên mục tiêu này vẫn được giữ nguyên
dưới tên All
. Phương thức triển khai cơ sở của SearchIndexProvider
nhằm đủ cho hầu hết các mảnh.
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; } }
Lập chỉ mục mảnh mới
Với thiết kế này, bạn có thể dễ dàng thêm một SettingsFragment
mới
được lập chỉ mục, thường là nội dung cập nhật hai dòng cung cấp XML cho mảnh và
ý định cần theo dõi. Với WifiSettingsFragment
là ví dụ:
@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); }
Việc triển khai AAOS của SearchIndexablesProvider
,
sử dụng SearchIndexableResources
và thực hiện bản dịch từ
SearchIndexProviders
vào giản đồ cơ sở dữ liệu cho
SettingsIntelligence
, nhưng không xác định được mảnh là gì
đang được lập chỉ mục. SettingsIntelligence
hỗ trợ số lượng nhà cung cấp bất kỳ, vì vậy, bạn có thể tạo nhà cung cấp mới để hỗ trợ các trường hợp sử dụng chuyên biệt, cho phép mỗi nhà cung cấp chuyên biệt và tập trung vào kết quả có cấu trúc tương tự. Các lựa chọn ưu tiên được xác định trong PreferenceScreen
cho mỗi mảnh phải được gán một khoá duy nhất để
được lập chỉ mục. Ngoài ra, PreferenceScreen
phải có khoá
được gán cho tiêu đề màn hình sẽ được lập chỉ mục.
Ví dụ về chỉ mục
Trong một số trường hợp, một mảnh có thể không có hành động theo ý định cụ thể liên kết với mảnh đó. Trong những trường hợp như vậy, bạn có thể truyền lớp hoạt động vào ý định CarBaseSearchIndexProvider
bằng cách sử dụng một thành phần thay vì một hành động. Thao tác này vẫn yêu cầu hoạt động phải có trong tệp kê khai
và có thể được xuất ra.
@SearchIndexable public class LanguagesAndInputFragment extends SettingsFragment { [...] public static final CarBaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new CarBaseSearchIndexProvider(R.xml.languages_and_input_fragment, LanguagesAndInputActivity.class); }
Trong một số trường hợp đặc biệt, một số phương thức của CarBaseSearchIndexProvider
có thể cần được ghi đè để kết quả mong muốn được lập chỉ mục. Ví dụ: trong NetworkAndInternetFragment
, các lựa chọn ưu tiên liên quan đến mạng di động sẽ không được lập chỉ mục trên các thiết bị không có mạng di động. Trong trường hợp này, hãy ghi đè phương thức getNonIndexableKeys
và đánh dấu các khoá thích hợp là không thể lập chỉ mục khi thiết bị không có mạng di động.
@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; } }; }
Tuỳ thuộc vào nhu cầu của mảnh cụ thể, các phương thức khác của trình
CarBaseSearchIndexProvider
có thể bị ghi đè để bao gồm các thuộc tính khác
dữ liệu có thể lập chỉ mục, chẳng hạn như dữ liệu thô tĩnh và động.
@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; } }; }
Chế độ cài đặt chèn chỉ mục
Cách chèn chế độ cài đặt để được lập chỉ mục:
- Xác định
SearchIndexablesProvider
cho ứng dụng của bạn bằng cách mở rộng Lớpandroid.provider.SearchIndexablesProvider
. - Cập nhật
AndroidManifest.xml
của ứng dụng bằng nhà cung cấp ở Bước 1. Định dạng như sau:<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>
-
Thêm dữ liệu có thể lập chỉ mục vào nhà cung cấp của bạn. Việc triển khai phụ thuộc vào nhu cầu của ứng dụng. Bạn có thể lập chỉ mục hai loại dữ liệu khác nhau:
SearchIndexableResource
vàSearchIndexableRaw
.
Ví dụ về 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; } } }