การจัดทำดัชนีการค้นหาการตั้งค่ารถยนต์

การค้นหาการตั้งค่าทำให้คุณสามารถค้นหาและเปลี่ยนแปลงการตั้งค่าเฉพาะในแอปการตั้งค่ายานยนต์ได้อย่างรวดเร็วและง่ายดาย โดยไม่ต้องไปที่เมนูแอปเพื่อค้นหา การค้นหาเป็นวิธีที่มีประสิทธิภาพที่สุดในการค้นหาการตั้งค่าเฉพาะ ตามค่าเริ่มต้น การค้นหาจะค้นหาการตั้งค่า 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 สำหรับแฟรกเมนต์ทั้งหมด มีการสนับสนุนสำหรับการระบุเป้าหมายเฉพาะสำหรับคำอธิบายประกอบ (เช่น Auto, TV และ 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 CarBaseSearchIndexProvider โดยใช้ส่วนประกอบแทนการกระทำ ซึ่งยังคงต้องมีกิจกรรมปรากฏในไฟล์ Manifest และเพื่อให้สามารถส่งออกได้

@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;
        }
    }
}