אינדקס חיפוש הגדרות רכב

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

אינדקס הגדרות המוזרקות

כדי להחדיר הגדרה לאינדקס:

  1. הגדר SearchIndexablesProvider עבור האפליקציה שלך על ידי הרחבת המחלקה android.provider.SearchIndexablesProvider .
  2. עדכן את 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>
    
  3. הוסף נתונים הניתנים לאינדקס לספק שלך. היישום תלוי בצרכי האפליקציה. ניתן להוסיף שני סוגי נתונים שונים: 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;
        }
    }
}