חיפוש בהגדרות מאפשר לך לחפש ולשנות הגדרות ספציפיות במהירות ובקלות בהגדרות של האפליקציה Automotive Settings (הגדרות כלי רכב) בלי לעבור דרך תפריטי האפליקציות כדי למצוא אותו. החיפוש הוא הדרך היעילה ביותר למצוא הגדרה ספציפית. כברירת מחדל, החיפוש מוצא רק הגדרות AOSP. הגדרות נוספות, גם אם הוחדרו וגם אם לא. מחייבים שינויים נוספים כדי שנוסיף לאינדקס.
הדרישות
כדי שאפשר יהיה להוסיף הגדרה לאינדקס באמצעות חיפוש בהגדרות, הנתונים צריכים להגיע:
- מקטע
SearchIndexable
בתוךCarSettings
. - אפליקציה ברמת המערכת.
הגדרת הנתונים
שדות נפוצים:
Key
(חובה) מפתח מחרוזת ייחודי שקריא לבני אדם לצורך זיהוי התוצאה.IconResId
אופציונלי אם מופיע סמל באפליקציה לצד אחרת, מוסיפים את מזהה המשאב. אחרת, מתעלמים מזה.IntentAction
חובה אםIntentTargetPackage
אוIntentTargetClass
לא מוגדר. מגדיר את הפעולה שתוצאת החיפוש היא לקחת.IntentTargetPackage
חובה אם הערךIntentAction
לא מוגדר. מגדיר את החבילה שכוונת תוצאת החיפוש צריכה לפתור.IntentTargetClass
חובה אם הערךIntentAction
לא מוגדר. מגדירה את הסיווג (פעילות) שכוונת תוצאת החיפוש צריכה לפתור.
SearchIndexableResource
בלבד:
XmlResId
(חובה) מגדיר את מזהה משאב ה-XML של הדף שמכיל את התוצאות לאינדקס.
SearchIndexableRaw
בלבד:
Title
(חובה) כותרת של תוצאת החיפוש.SummaryOn
(אופציונלי) סיכום של תוצאת החיפוש.Keywords
(אופציונלי) רשימת מילים שמשויכות לתוצאת החיפוש. מתאימה את השאילתה לתוצאה.ScreenTitle
(אופציונלי) כותרת הדף עם תוצאת החיפוש.
הסתרת הנתונים
כל תוצאת חיפוש מופיעה בחיפוש, אלא אם מסומן אחרת. במצב סטטי תוצאות החיפוש נשמרות במטמון, בכל פעם שמאוחזרת רשימה חדשה של מפתחות שלא ניתן להוסיף לאינדקס החיפוש נפתח. הסיבות להסתרת התוצאות עשויות לכלול:
- עותק כפול. לדוגמה, הוא מופיע במספר דפים.
- מוצגים רק באופן מותנה. לדוגמה, מוצגות רק ההגדרות של חבילת גלישה כשיש כרטיס SIM).
- דף תבנית. לדוגמה, דף פרטים של אפליקציה ספציפית.
- ההגדרה צריכה הקשר נוסף מאשר שם וכתוביות. עבור לדוגמה, השם "הגדרות" להגדרה הזו, שרלוונטית רק לכותרת המסך.
כדי להסתיר הגדרה, הספק או 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
, שהיא wrapper צר
סביב רשימת השדות SearchIndexProvider
לכל המקטעים.
ניתנת תמיכה בציון יעד ספציפי של הערה (כמו
אוטומטי, טלוויזיה ו-Wear), עם זאת, רוב ההערות נותרות כברירת המחדל (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
חייב להיות מפתח
הוקצה ל- כדי שכותרת המסך תתווסף לאינדקס.
דוגמאות לאינדקס
במקרים מסוימים, ייתכן שלא תהיה שיוך של פעולת Intent ספציפית למקטע
איתו. במקרים כאלה, אפשר להעביר את קטגוריית הפעילות אל
Intent אחד (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; } }; }
הגדרות של החדרה לאינדקס
כדי להחדיר הגדרה להוספה לאינדקס:
- כדי להגדיר
SearchIndexablesProvider
לאפליקציה שלך, אפשר להרחיב את הקטע כיתה אחת (android.provider.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; } } }