חיפוש הגדרות מאפשר לך לחפש ולשנות במהירות ובקלות הגדרות ספציפיות באפליקציית הגדרות הרכב מבלי לנווט בתפריטי האפליקציה כדי למצוא אותה. חיפוש הוא הדרך היעילה ביותר למצוא הגדרה ספציפית. כברירת מחדל, החיפוש מוצא הגדרות 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
שנוצרה אוטומטית, שהיא עטיפה דקה סביב רשימת השדות של 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
יש להקצות מפתח כדי שכותרת המסך תתווסף לאינדקס.
דוגמאות אינדקס
במקרים מסוימים, ייתכן שלפרגמנט אין פעולת כוונה ספציפית הקשורה אליו. במקרים כאלה, אפשר לעבור במחלקת הפעילות לתוך הכוונה 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; } } }