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, cho dù có được chèn hay không, yêu cầu thay đổi bổ sung để được lập chỉ mục.
Yêu cầu
Để có thể lập chỉ mục một chế độ cài đặt 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ả, sau đó thêm mã 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à kết quả tìm kiếm ý định là muốn nhậ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ã tài nguyên XML của trang chứa kết quả được lập chỉ mục.
Chỉ dành cho SearchIndexableRaw
:
Title
. (Bắt buộc) Tiêu đề của kết quả tìm kiếm.SummaryOn
. (Không bắt buộc) Bản tóm tắt của 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ở. Lý do ẩn kết quả có thể bao gồm:
- 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 theo 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ụ đề. Cho ví dụ: "Cài đặt" mà chỉ liên quan đến tiêu đề màn hình.
Để ẩn một chế độ cài đặt, nhà cung cấp của bạn hoặc SEARCH_INDEX_DATA_PROVIDER
nên
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 thành phần 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ính năng tìm kiếm trong mục Cài đặt sẽ cập nhật danh sách động này khi đã 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 trên ô 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
. SettingsLib
và android.provider
các gói đã có giao diện được định nghĩa và lớp trừu tượng để mở rộng
cung cấp các mục mới cần được lập chỉ mục. Trong chế độ cài đặt AOSP, việc triển khai các cấu hình 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 kết quả SearchIndexProvider
dẫn đến quá trình 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 được thêm vào thư mục được tạo tự động
Lớp SearchIndexableResourcesAuto
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
vớ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ột 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ợ bất kỳ số lượng
các nhà cung cấp khác để tạo ra các nhà cung cấp mới nhằm hỗ trợ việc sử dụng
trường hợp, cho phép mỗi chỉ số được chuyên biệt và tập trung vào kết quả có
cấu trúc. 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
đượ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ó thao tác theo ý định cụ thể nào được liên kết
với nó. Trong những trường hợp như vậy, có thể chuyển lớp hoạt động vào
Ý định CarBaseSearchIndexProvider
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ể xuất khẩu.
@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 là
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 đè
getNonIndexableKeys
rồi đánh dấu các khoá thích hợp là
không lập chỉ mục đượ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 được chèn chỉ mục
Để chèn một chế độ cài đặt cần được lập chỉ mục, hãy làm như sau:
- 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. Có thể lập chỉ mục hai loại dữ liệu sau đây:
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; } } }