جستجوی تنظیمات به شما امکان میدهد تا به سرعت و به راحتی تنظیمات خاصی را در برنامه تنظیمات خودرو بدون پیمایش در منوهای برنامه برای یافتن آن جستجو کرده و تغییر دهید. جستجو موثرترین راه برای یافتن یک تنظیم خاص است. به طور پیش فرض، جستجو فقط تنظیمات AOSP را پیدا می کند. تنظیمات اضافی، چه تزریقی یا نه، برای ایندکس شدن نیاز به تغییرات اضافی دارند.
الزامات
برای اینکه یک تنظیم با جستجوی تنظیمات نمایه شود، دادهها باید از یک عبارت زیر باشند:
-
SearchIndexable
بخش قابل نمایه سازی داخلCarSettings
. - برنامه در سطح سیستم
داده ها را تعریف کنید
زمینه های مشترک:
-
Key
. ( الزامی ) کلید رشته قابل خواندن منحصر به فرد برای شناسایی نتیجه. -
IconResId
. اختیاری اگر نمادی در برنامه شما در کنار نتیجه ظاهر شد، شناسه منبع را اضافه کنید، در غیر این صورت نادیده بگیرید. -
IntentAction
. اگرIntentTargetPackage
یاIntentTargetClass
تعریف نشده باشد، الزامی است . اقدامی را که نتیجه جستجو قرار است انجام دهد را مشخص می کند. -
IntentTargetPackage
. اگرIntentAction
تعریف نشده باشد، الزامی است . بسته ای را تعریف می کند که هدف نتیجه جستجو برای حل آن است. -
IntentTargetClass
. اگرIntentAction
تعریف نشده باشد، الزامی است . کلاس (فعالیتی) را که هدف نتیجه جستجو برای حل آن است، تعریف می کند.
فقط SearchIndexableResource
:
-
XmlResId
. ( الزامی ) شناسه منبع XML صفحه حاوی نتایجی که قرار است نمایه شوند را تعریف می کند.
فقط SearchIndexableRaw
:
-
Title
. ( الزامی ) عنوان نتیجه جستجو. -
SummaryOn
. ( اختیاری ) خلاصه ای از نتیجه جستجو. -
Keywords
( اختیاری ) فهرست کلمات مرتبط با نتیجه جستجو. پرس و جو را با نتیجه شما مطابقت می دهد. -
ScreenTitle
( اختیاری ) عنوان صفحه با نتیجه جستجوی شما.
مخفی کردن داده ها
هر نتیجه جستجو در «جستجو» ظاهر میشود، مگر اینکه به شکل دیگری علامتگذاری شده باشد. در حالی که نتایج جستجوی ایستا در حافظه پنهان ذخیره می شوند، هر بار که جستجو باز می شود، فهرست جدیدی از کلیدهای غیرقابل نمایه سازی بازیابی می شود. دلایل پنهان کردن نتایج می تواند شامل موارد زیر باشد:
- تکراری. به عنوان مثال، در چندین صفحه ظاهر می شود.
- فقط مشروط نشان داده شده است. به عنوان مثال، تنظیمات داده تلفن همراه را فقط در صورت وجود سیم کارت نشان می دهد.
- صفحه قالب. به عنوان مثال، یک صفحه جزئیات برای یک برنامه جداگانه.
- تنظیم نیاز به زمینه بیشتری نسبت به عنوان و زیرنویس دارد. به عنوان مثال، یک تنظیم "تنظیمات" که فقط مربوط به عنوان صفحه است.
برای پنهان کردن یک تنظیم، ارائهدهنده شما یا SEARCH_INDEX_DATA_PROVIDER
باید کلید نتیجه جستجو را از getNonIndexableKeys
برگرداند. کلید همیشه می تواند برگردانده شود (تکراری، موارد صفحه قالب) یا به صورت مشروط اضافه شود (بدون مورد داده تلفن همراه).
شاخص استاتیک
اگر داده های شاخص شما همیشه یکسان است از شاخص استاتیک استفاده کنید. به عنوان مثال، عنوان و خلاصه داده های XML یا داده های خام کد سخت. هنگامی که جستجوی تنظیمات برای اولین بار راه اندازی می شود، داده های استاتیک تنها یک بار نمایه می شوند.
برای فهرستپذیرهای داخل تنظیمات، getXmlResourcesToIndex
و/یا getRawDataToIndex
را پیادهسازی کنید. برای تنظیمات تزریق شده، روشهای queryXmlResources
و/یا queryRawData
را پیادهسازی کنید.
شاخص پویا
اگر می توان داده های قابل نمایه سازی را بر این اساس به روز کرد، از روش پویا برای فهرست بندی داده های خود استفاده کنید. جستجوی تنظیمات این فهرست پویا را هنگام راهاندازی بهروزرسانی میکند.
برای فهرستپذیریهای داخل تنظیمات، getDynamicRawDataToIndex
را پیادهسازی کنید. برای تنظیمات تزریق شده، queryDynamicRawData methods
را پیاده سازی کنید.
فهرست در تنظیمات خودرو
برای فهرست بندی تنظیمات مختلف برای گنجاندن در ویژگی جستجو، به ارائه دهندگان محتوا مراجعه کنید. بستههای SettingsLib
و android.provider
از قبل دارای رابطهای تعریفشده و کلاسهای انتزاعی هستند تا برای ارائه ورودیهای جدید نمایهسازی شوند. در تنظیمات AOSP، از پیاده سازی این کلاس ها برای فهرست بندی نتایج استفاده می شود. رابط اصلی که باید انجام شود SearchIndexablesProvider
است که توسط SettingsIntelligence
برای فهرست کردن داده ها استفاده می شود.
public abstract class SearchIndexablesProvider extends ContentProvider { public abstract Cursor queryXmlResources(String[] projection); public abstract Cursor queryRawData(String[] projection); public abstract Cursor queryNonIndexableKeys(String[] projection); }
در تئوری، هر قطعه می تواند به نتایج در SearchIndexablesProvider
اضافه شود، در این صورت SettingsIntelligence
محتوا خواهد بود. برای آسان کردن فرآیند نگهداری آسان برای افزودن قطعات جدید، از تولید کد SettingsLib
SearchIndexableResources
استفاده کنید. مختص تنظیمات خودرو، هر قطعه قابل نمایهسازی با @SearchIndexable
حاشیهنویسی میشود و سپس دارای یک فیلد استاتیک SearchIndexProvider
است که دادههای مربوطه را برای آن قطعه ارائه میکند. قطعات دارای حاشیه نویسی اما فاقد SearchIndexProvider
هستند منجر به یک خطای کامپایل می شوند.
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); }
سپس تمام این قطعات به کلاس SearchIndexableResourcesAuto
تولید شده به طور خودکار اضافه می شوند، که یک پوشش نازک در اطراف لیست فیلدهای SearchIndexProvider
برای همه قطعات است. پشتیبانی برای تعیین یک هدف خاص برای یک حاشیه نویسی (مانند خودکار، تلویزیون، و پوشیدن) ارائه می شود، اما بیشتر حاشیه نویسی ها در حالت پیش فرض باقی می مانند ( All
). در این مورد، هیچ نیاز خاصی برای تعیین هدف خودکار وجود ندارد، بنابراین به صورت All
باقی میماند. پیاده سازی پایه SearchIndexProvider
برای اکثر قطعات کافی است.
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; } }
یک قطعه جدید را فهرست کنید
با این طراحی، اضافه کردن SettingsFragment
جدید برای نمایه سازی نسبتاً آسان است، معمولاً یک به روز رسانی دو خطی XML را برای قطعه و هدفی که باید دنبال شود ارائه می دهد. با WifiSettingsFragment
به عنوان مثال:
@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); }
اجرای AAOS SearchIndexablesProvider
، که از SearchIndexableResources
استفاده میکند و ترجمه را از SearchIndexProviders
به طرحواره پایگاه داده برای SettingsIntelligence
انجام میدهد، اما نسبت به قطعاتی که نمایهسازی میشوند، ناشناس است. SettingsIntelligence
از هر تعداد ارائهدهنده پشتیبانی میکند، بنابراین میتوان ارائهدهندگان جدیدی را برای پشتیبانی از موارد استفاده تخصصی ایجاد کرد که به هر یک از آنها اجازه میدهد تخصصی و بر نتایج با ساختارهای مشابه متمرکز شوند. تنظیمات برگزیده تعریف شده در PreferenceScreen
برای قطعه باید هر کدام یک کلید منحصر به فرد داشته باشند تا ایندکس شوند. علاوه بر این، PreferenceScreen
باید دارای کلیدی باشد تا عنوان صفحه نمایش داده شود.
نمونه های شاخص
در برخی موارد، یک قطعه ممکن است یک کنش هدف خاص مرتبط با آن نداشته باشد. در چنین مواردی، میتوان با استفاده از یک مؤلفه به جای یک عمل، کلاس فعالیت را به هدف CarBaseSearchIndexProvider
منتقل کرد. برای این کار همچنان نیاز است که فعالیت در فایل مانیفست وجود داشته باشد و آن را صادر کند.
@SearchIndexable public class LanguagesAndInputFragment extends SettingsFragment { [...] public static final CarBaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new CarBaseSearchIndexProvider(R.xml.languages_and_input_fragment, LanguagesAndInputActivity.class); }
در برخی موارد خاص، برخی از روشهای CarBaseSearchIndexProvider
ممکن است لازم باشد برای نمایهسازی نتایج مورد نظر، نادیده گرفته شوند. برای مثال، در NetworkAndInternetFragment
، ترجیحات مربوط به شبکه تلفن همراه نباید در دستگاههای بدون شبکه تلفن همراه ایندکس شود. در این مورد، روش getNonIndexableKeys
را نادیده بگیرید و هنگامی که دستگاهی شبکه تلفن همراه ندارد، کلیدهای مناسب را بهعنوان غیرقابل فهرستبندی علامتگذاری کنید.
@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; } }; }
بسته به نیاز یک قطعه خاص، روشهای دیگر CarBaseSearchIndexProvider
ممکن است برای گنجاندن سایر دادههای قابل نمایهسازی، مانند دادههای خام استاتیک و پویا، لغو شوند.
@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; } }; }
تنظیمات تزریق شده را فهرست کنید
برای تزریق تنظیماتی که باید نمایه شود:
- با گسترش کلاس
android.provider.SearchIndexablesProvider
برای برنامه خود یکSearchIndexablesProvider
تعریف کنید. -
AndroidManifest.xml
برنامه را با ارائه دهنده در مرحله 1 به روز کنید. قالب این است:<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>
- داده های قابل نمایه سازی را به ارائه دهنده خود اضافه کنید. پیاده سازی بستگی به نیازهای برنامه دارد. دو نوع داده مختلف را می توان ایندکس کرد:
SearchIndexableResource
وSearchIndexableRaw
.
مثال 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; } } }