涉及代码:
packages\apps\Settings\src\com\android\settings

frameworks\base\core\java\android\provider\

在改bug的时候,涉及到这块,发现流程没有弄清楚,趁着有时间,理一理。

一.SettingsActivity.java

    @Override    protected void onCreate(Bundle savedState) {        super.onCreate(savedState);        ...        if (mIsShowingDashboard) {            // Run the Index update only if we have some space            if (!Utils.isLowStorage(this)) {                Index.getInstance(getApplicationContext()).update();//1            } else {                Log.w(LOG_TAG, "Cannot update the Indexer as we are running low on storage space!");            }        }      ...    }    @Override    public void onConfigurationChanged(Configuration newConfig) {        super.onConfigurationChanged(newConfig);        Index.getInstance(this).update();//2    }

在注释1和注释2的地方都调用了Index的update()方法,它是快速搜索中对数据进行处理的入口。

二.Index.java

    public void update() {        final Intent intent = new Intent(SearchIndexablesContract.PROVIDER_INTERFACE);        List list =                mContext.getPackageManager().queryIntentContentProviders(intent, 0);//1        final int size = list.size();        for (int n = 0; n < size; n++) {            final ResolveInfo info = list.get(n);            if (!isWellKnownProvider(info)) {                continue;            }            final String authority = info.providerInfo.authority;            final String packageName = info.providerInfo.packageName;            addIndexablesFromRemoteProvider(packageName, authority);//2            addNonIndexablesKeysFromRemoteProvider(packageName, authority);//3        }        updateInternal();//4    }

SearchIndexablesContract.java

    public static final String PROVIDER_INTERFACE =            "android.content.action.SEARCH_INDEXABLES_PROVIDER";

AndroidManifest.xml

                                                

注释1处会遍历所有注册了action为android.content.action.SEARCH_INDEXABLES_PROVIDER的provider,
我这里先只考虑设置应用本身的provider,也即是SettingsSearchIndexablesProvider.
注释2处会添加需要被搜索到的设置项
注释3处会添加不需被搜索到设置项
注释4处会调用updateInternal将该对象更新到数据库中

三.Index.java

    private static Uri buildUriForXmlResources(String authority) {        return Uri.parse("content://" + authority + "/" +                SearchIndexablesContract.INDEXABLES_XML_RES_PATH);    }    private boolean addIndexablesFromRemoteProvider(String packageName, String authority) {        try {            final int baseRank = Ranking.getBaseRankForAuthority(authority);            final Context context = mBaseAuthority.equals(authority) ?                    mContext : mContext.createPackageContext(packageName, 0);            final Uri uriForResources = buildUriForXmlResources(authority);//1            addIndexablesForXmlResourceUri(context, packageName, uriForResources,                    SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS, baseRank);            final Uri uriForRawData = buildUriForRawData(authority);            addIndexablesForRawDataUri(context, packageName, uriForRawData,                    SearchIndexablesContract.INDEXABLES_RAW_COLUMNS, baseRank);            return true;        } catch (PackageManager.NameNotFoundException e) {            Log.w(LOG_TAG, "Could not create context for " + packageName + ": "                    + Log.getStackTraceString(e));            return false;        }    }    private void addIndexablesForXmlResourceUri(Context packageContext, String packageName,            Uri uri, String[] projection, int baseRank) {        final ContentResolver resolver = packageContext.getContentResolver();        final Cursor cursor = resolver.query(uri, projection, null, null, null);//2        if (cursor == null) {            Log.w(LOG_TAG, "Cannot add index data for Uri: " + uri.toString());            return;        }        try {            final int count = cursor.getCount();            if (count > 0) {                while (cursor.moveToNext()) {                    final int providerRank = cursor.getInt(COLUMN_INDEX_XML_RES_RANK);                    final int rank = (providerRank > 0) ? baseRank + providerRank : baseRank;                    final int xmlResId = cursor.getInt(COLUMN_INDEX_XML_RES_RESID);                    final String className = cursor.getString(COLUMN_INDEX_XML_RES_CLASS_NAME);                    final int iconResId = cursor.getInt(COLUMN_INDEX_XML_RES_ICON_RESID);                    final String action = cursor.getString(COLUMN_INDEX_XML_RES_INTENT_ACTION);                    final String targetPackage = cursor.getString(                            COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE);                    final String targetClass = cursor.getString(                            COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS);                    SearchIndexableResource sir = new SearchIndexableResource(packageContext);                    sir.rank = rank;                    sir.xmlResId = xmlResId;                    sir.className = className;                    sir.packageName = packageName;                    sir.iconResId = iconResId;                    sir.intentAction = action;                    sir.intentTargetPackage = targetPackage;                    sir.intentTargetClass = targetClass;                    addIndexableData(sir);//3                }            }        } finally {            cursor.close();        }    }public void addIndexableData(SearchIndexableData data) {        synchronized (mDataToProcess) {            mDataToProcess.dataToUpdate.add(data);        }    }

SearchIndexablesContract.java

    /**     * Indexable reference names.     */    public static final String INDEXABLES_XML_RES = "indexables_xml_res";    /**     * ContentProvider path for indexable xml resources.     */    public static final String INDEXABLES_XML_RES_PATH = SETTINGS + "/" + INDEXABLES_XML_RES;

注释1处构造了一个uri,content://com.android.settings/settings/indexables_xml_res
注释2处查询此uri的数据,uri中的com.android.settings 对应的就是android:authorities="com.android.settings",通过此我们可以知道对应的provider为SettingsSearchIndexablesProvider
注释3处将查询到结果添加到内存中的mDataToProcess

四.SettingsSearchIndexablesProvider.java

public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider {    private static final String TAG = "SettingsSearchIndexablesProvider";    @Override    public boolean onCreate() {        return true;    }    @Override    public Cursor queryXmlResources(String[] projection) {        MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS);        Collection values = SearchIndexableResources.values();//3        for (SearchIndexableResource val : values) {            Object[] ref = new Object[7];            ref[COLUMN_INDEX_XML_RES_RANK] = val.rank;            ref[COLUMN_INDEX_XML_RES_RESID] = val.xmlResId;            ref[COLUMN_INDEX_XML_RES_CLASS_NAME] = val.className;            ref[COLUMN_INDEX_XML_RES_ICON_RESID] = val.iconResId;            /* SPRD 494558 ,add the search for Timer Switch Machine            ref[COLUMN_INDEX_XML_RES_INTENT_ACTION] = null; // intent action            ref[COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE] = null; // intent target package            ref[COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS] = null; // intent target class            @{ */            ref[COLUMN_INDEX_XML_RES_INTENT_ACTION] = val.intentAction;//intent action            ref[COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE] = val.intentTargetPackage;//intent target package            ref[COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS] = val.intentTargetClass;//intent targetclass            /* @} */            cursor.addRow(ref);        }        return cursor;    }    }

SearchIndexablesProvider .java

public abstract class SearchIndexablesProvider extends ContentProvider {    private static final String TAG = "IndexablesProvider";    private String mAuthority;    private UriMatcher mMatcher;    private static final int MATCH_RES_CODE = 1;    private static final int MATCH_RAW_CODE = 2;    private static final int MATCH_NON_INDEXABLE_KEYS_CODE = 3;    /**     * Implementation is provided by the parent class.     */    @Override    public void attachInfo(Context context, ProviderInfo info) {        mAuthority = info.authority;        mMatcher = new UriMatcher(UriMatcher.NO_MATCH);        mMatcher.addURI(mAuthority, SearchIndexablesContract.INDEXABLES_XML_RES_PATH,                MATCH_RES_CODE);//1        mMatcher.addURI(mAuthority, SearchIndexablesContract.INDEXABLES_RAW_PATH,                MATCH_RAW_CODE);        mMatcher.addURI(mAuthority, SearchIndexablesContract.NON_INDEXABLES_KEYS_PATH,                MATCH_NON_INDEXABLE_KEYS_CODE);        // Sanity check our setup        if (!info.exported) {            throw new SecurityException("Provider must be exported");        }        if (!info.grantUriPermissions) {            throw new SecurityException("Provider must grantUriPermissions");        }        if (!android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(info.readPermission)) {            throw new SecurityException("Provider must be protected by READ_SEARCH_INDEXABLES");        }        super.attachInfo(context, info);    }    @Override    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,                        String sortOrder) {        switch (mMatcher.match(uri)) {            case MATCH_RES_CODE:                return queryXmlResources(null);//2            case MATCH_RAW_CODE:                return queryRawData(null);            case MATCH_NON_INDEXABLE_KEYS_CODE:                return queryNonIndexableKeys(null);            default:                throw new UnsupportedOperationException("Unknown Uri " + uri);        }    }

注释1处对uri进行了匹配
注释2处匹配成功后调用queryXmlResources()
注释3处获取所有SearchIndexableResource对象,最后加入到cursor的row中

五.SearchIndexableResources.java

private static HashMap sResMap =            new HashMap();    static {        sResMap.put(WifiSettings.class.getName(),                new SearchIndexableResource(                        Ranking.getRankForClassName(WifiSettings.class.getName()),                        NO_DATA_RES_ID,                        WifiSettings.class.getName(),                        R.drawable.ic_settings_wireless));        ...        sResMap.put(SavedAccessPointsWifiSettings.class.getName(),                new SearchIndexableResource(                        Ranking.getRankForClassName(SavedAccessPointsWifiSettings.class.getName()),                        R.xml.wifi_display_saved_access_points,                        SavedAccessPointsWifiSettings.class.getName(),                        R.drawable.ic_settings_wireless));//1        sResMap.put(DisplaySettings.class.getName(),                new SearchIndexableResource(                        Ranking.getRankForClassName(DisplaySettings.class.getName()),                        NO_DATA_RES_ID,                        DisplaySettings.class.getName(),                        R.drawable.ic_settings_display));//2        ...     }     public static Collection values() {        return sResMap.values();     }

注释1处对应的投射作为主项存在,即投射显示在标题上
注释2处对应的投射作为子项存在,即投射显示在内容上
bug中移除搜索中的投射,可以将注释1的地方注释掉,注释2不能简单移除,要继续走下去
到此为止,数据已经获取到,但获取到的数据量实在太少,仔细看可以看到大部分只是设置界面一级目录数据,那二级目录及其以后数据在哪获取呢,接着看update()方法中的updateInternal()

六.IndexDatabaseHelper .java

private static final String DATABASE_NAME = "search_index.db";public interface Tables {        public static final String TABLE_PREFS_INDEX = "prefs_index";        public static final String TABLE_META_INDEX = "meta_index";        public static final String TABLE_SAVED_QUERIES = "saved_queries";    }    public static synchronized IndexDatabaseHelper getInstance(Context context) {        if (sSingleton == null) {            sSingleton = new IndexDatabaseHelper(context);        }        return sSingleton;    }    public IndexDatabaseHelper(Context context) {        super(context, DATABASE_NAME, null, DATABASE_VERSION);    }    @Override    public void onCreate(SQLiteDatabase db) {        bootstrapDB(db);    }    private void bootstrapDB(SQLiteDatabase db) {        db.execSQL(CREATE_INDEX_TABLE);        db.execSQL(CREATE_META_TABLE);        db.execSQL(CREATE_SAVED_QUERIES_TABLE);        db.execSQL(INSERT_BUILD_VERSION);        Log.i(TAG, "Bootstrapped database");    }

Index.java

    private void updateInternal() {        synchronized (mDataToProcess) {            final UpdateIndexTask task = new UpdateIndexTask();            UpdateData copy = mDataToProcess.copy();            task.execute(copy);            mDataToProcess.clear();        }    }    private class UpdateIndexTask extends AsyncTask {        @Override        protected Void doInBackground(UpdateData... params) {            final List dataToUpdate = params[0].dataToUpdate;            final List dataToDelete = params[0].dataToDelete;            final Map> nonIndexableKeys = params[0].nonIndexableKeys;            final boolean forceUpdate = params[0].forceUpdate;            final SQLiteDatabase database = getWritableDatabase();//1            if (database == null) {                Log.e(LOG_TAG, "Cannot update Index as I cannot get a writable database");                return null;            }            final String localeStr = Locale.getDefault().toString();            try {                database.beginTransaction();                if (dataToDelete.size() > 0) {                    processDataToDelete(database, localeStr, dataToDelete);                }                if (dataToUpdate.size() > 0) {                    processDataToUpdate(database, localeStr, dataToUpdate, nonIndexableKeys,                            forceUpdate);                }                database.setTransactionSuccessful();            } finally {                database.endTransaction();            }            return null;        }        private boolean processDataToUpdate(SQLiteDatabase database, String localeStr,                List dataToUpdate, Map> nonIndexableKeys,                boolean forceUpdate) {            if (!forceUpdate && isLocaleAlreadyIndexed(database, localeStr)) {                Log.d(LOG_TAG, "Locale '" + localeStr + "' is already indexed");                return true;            }            boolean result = false;            final long current = System.currentTimeMillis();            final int count = dataToUpdate.size();            for (int n = 0; n < count; n++) {                final SearchIndexableData data = dataToUpdate.get(n);                try {                    indexOneSearchIndexableData(database, localeStr, data, nonIndexableKeys);//2                } catch (Exception e) {                    Log.e(LOG_TAG,                            "Cannot index: " + data.className + " for locale: " + localeStr, e);                }            }            final long now = System.currentTimeMillis();            Log.d(LOG_TAG, "Indexing locale '" + localeStr + "' took " +                    (now - current) + " millis");            return result;        }    }private void indexOneSearchIndexableData(SQLiteDatabase database, String localeStr,            SearchIndexableData data, Map> nonIndexableKeys) {        if (data instanceof SearchIndexableResource) {            indexOneResource(database, localeStr, (SearchIndexableResource) data, nonIndexableKeys);//3        } else if (data instanceof SearchIndexableRaw) {            indexOneRaw(database, localeStr, (SearchIndexableRaw) data);        }    }private void indexOneResource(SQLiteDatabase database, String localeStr,            SearchIndexableResource sir, Map> nonIndexableKeysFromResource) {        if (sir == null) {            Log.e(LOG_TAG, "Cannot index a null resource!");            return;        }        final List nonIndexableKeys = new ArrayList();        if (sir.xmlResId > SearchIndexableResources.NO_DATA_RES_ID) {            List resNonIndxableKeys = nonIndexableKeysFromResource.get(sir.packageName);            if (resNonIndxableKeys != null && resNonIndxableKeys.size() > 0) {                nonIndexableKeys.addAll(resNonIndxableKeys);            }            indexFromResource(sir.context, database, localeStr,                    sir.xmlResId, sir.className, sir.iconResId, sir.rank,                    sir.intentAction, sir.intentTargetPackage, sir.intentTargetClass,                    nonIndexableKeys);        } else {            if (TextUtils.isEmpty(sir.className)) {                Log.w(LOG_TAG, "Cannot index an empty Search Provider name!");                return;            }            final Class<?> clazz = getIndexableClass(sir.className);//8            if (clazz == null) {                Log.d(LOG_TAG, "SearchIndexableResource '" + sir.className +                        "' should implement the " + Indexable.class.getName() + " interface!");                return;            }            // Will be non null only for a Local provider implementing a            // SEARCH_INDEX_DATA_PROVIDER field            final Indexable.SearchIndexProvider provider = getSearchIndexProvider(clazz);//9            if (provider != null) {                List providerNonIndexableKeys = provider.getNonIndexableKeys(sir.context);                if (providerNonIndexableKeys != null && providerNonIndexableKeys.size() > 0) {                    nonIndexableKeys.addAll(providerNonIndexableKeys);                }                indexFromProvider(mContext, database, localeStr, provider, sir.className,                        sir.iconResId, sir.rank, sir.enabled, nonIndexableKeys);//4            }        }    }    private void indexFromProvider(Context context, SQLiteDatabase database, String localeStr,            Indexable.SearchIndexProvider provider, String className, int iconResId, int rank,            boolean enabled, List nonIndexableKeys) {        if (provider == null) {            Log.w(LOG_TAG, "Cannot find provider: " + className);            return;        }        final List rawList = provider.getRawDataToIndex(context, enabled);        if (rawList != null) {            final int rawSize = rawList.size();            for (int i = 0; i < rawSize; i++) {                SearchIndexableRaw raw = rawList.get(i);                // Should be the same locale as the one we are processing                if (!raw.locale.toString().equalsIgnoreCase(localeStr)) {                    continue;                }                if (nonIndexableKeys.contains(raw.key)) {                    continue;                }                updateOneRowWithFilteredData(database, localeStr,                        raw.title,                        raw.summaryOn,                        raw.summaryOff,                        raw.entries,                        className,                        raw.screenTitle,                        iconResId,                        rank,                        raw.keywords,                        raw.intentAction,                        raw.intentTargetPackage,                        raw.intentTargetClass,                        raw.enabled,                        raw.key,                        raw.userId);            }        }        final List resList =                provider.getXmlResourcesToIndex(context, enabled);        if (resList != null) {            final int resSize = resList.size();            for (int i = 0; i < resSize; i++) {                SearchIndexableResource item = resList.get(i);                // Should be the same locale as the one we are processing                if (!item.locale.toString().equalsIgnoreCase(localeStr)) {                    continue;                }                final int itemIconResId = (item.iconResId == 0) ? iconResId : item.iconResId;                final int itemRank = (item.rank == 0) ? rank : item.rank;                String itemClassName = (TextUtils.isEmpty(item.className))                        ? className : item.className;                indexFromResource(context, database, localeStr,                        item.xmlResId, itemClassName, itemIconResId, itemRank,                        item.intentAction, item.intentTargetPackage,                        item.intentTargetClass, nonIndexableKeys);//5            }        }    }    private void updateOneRowWithFilteredData(SQLiteDatabase database, String locale,            String title, String summaryOn, String summaryOff, String entries,            String className,            String screenTitle, int iconResId, int rank, String keywords,            String intentAction, String intentTargetPackage, String intentTargetClass,            boolean enabled, String key, int userId) {        final String updatedTitle = normalizeHyphen(title);        final String updatedSummaryOn = normalizeHyphen(summaryOn);        final String updatedSummaryOff = normalizeHyphen(summaryOff);        final String normalizedTitle = normalizeString(updatedTitle);        final String normalizedSummaryOn = normalizeString(updatedSummaryOn);        final String normalizedSummaryOff = normalizeString(updatedSummaryOff);        updateOneRow(database, locale,                updatedTitle, normalizedTitle, updatedSummaryOn, normalizedSummaryOn,                updatedSummaryOff, normalizedSummaryOff, entries,                className, screenTitle, iconResId,                rank, keywords, intentAction, intentTargetPackage, intentTargetClass, enabled,                key, userId);//6    }    private void updateOneRow(SQLiteDatabase database, String locale,            String updatedTitle, String normalizedTitle,            String updatedSummaryOn, String normalizedSummaryOn,            String updatedSummaryOff, String normalizedSummaryOff, String entries,            String className, String screenTitle, int iconResId, int rank, String keywords,            String intentAction, String intentTargetPackage, String intentTargetClass,            boolean enabled, String key, int userId) {        if (TextUtils.isEmpty(updatedTitle)) {            return;        }        // The DocID should contains more than the title string itself (you may have two settings        // with the same title). So we need to use a combination of the title and the screenTitle.        StringBuilder sb = new StringBuilder(updatedTitle);        sb.append(screenTitle);        int docId = sb.toString().hashCode();        ContentValues values = new ContentValues();        values.put(IndexColumns.DOCID, docId);        values.put(IndexColumns.LOCALE, locale);        values.put(IndexColumns.DATA_RANK, rank);        values.put(IndexColumns.DATA_TITLE, updatedTitle);        values.put(IndexColumns.DATA_TITLE_NORMALIZED, normalizedTitle);        values.put(IndexColumns.DATA_SUMMARY_ON, updatedSummaryOn);        values.put(IndexColumns.DATA_SUMMARY_ON_NORMALIZED, normalizedSummaryOn);        values.put(IndexColumns.DATA_SUMMARY_OFF, updatedSummaryOff);        values.put(IndexColumns.DATA_SUMMARY_OFF_NORMALIZED, normalizedSummaryOff);        values.put(IndexColumns.DATA_ENTRIES, entries);        values.put(IndexColumns.DATA_KEYWORDS, keywords);        values.put(IndexColumns.CLASS_NAME, className);        values.put(IndexColumns.SCREEN_TITLE, screenTitle);        values.put(IndexColumns.INTENT_ACTION, intentAction);        values.put(IndexColumns.INTENT_TARGET_PACKAGE, intentTargetPackage);        values.put(IndexColumns.INTENT_TARGET_CLASS, intentTargetClass);        values.put(IndexColumns.ICON, iconResId);        values.put(IndexColumns.ENABLED, enabled);        values.put(IndexColumns.DATA_KEY_REF, key);        values.put(IndexColumns.USER_ID, userId);        database.replaceOrThrow(Tables.TABLE_PREFS_INDEX, null, values);//7    }   private static Class<?> getIndexableClass(String className) {        final Class<?> clazz;        try {            clazz = Class.forName(className);        } catch (ClassNotFoundException e) {            Log.d(LOG_TAG, "Cannot find class: " + className);            return null;        }        return isIndexableClass(clazz) ? clazz : null;    }    private static final String FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER =            "SEARCH_INDEX_DATA_PROVIDER";    private Indexable.SearchIndexProvider getSearchIndexProvider(final Class<?> clazz) {        try {            final Field f = clazz.getField(FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER);            return (Indexable.SearchIndexProvider) f.get(null);        } catch (NoSuchFieldException e) {            Log.d(LOG_TAG, "Cannot find field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");        } catch (SecurityException se) {            Log.d(LOG_TAG,                    "Security exception for field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");        } catch (IllegalAccessException e) {            Log.d(LOG_TAG,                    "Illegal access to field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");        } catch (IllegalArgumentException e) {            Log.d(LOG_TAG,                    "Illegal argument when accessing field '" +                            FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");        }        return null;    }

注释1处获取数据库,数据库名称为search_index.db,
剩下的步骤顺着注释2,3,4,5,6,7走下去,最终走到注释7,对数据库数据进行更新。
需要注意以下两个地方:
以DisplaySettings为例,它的xmlResId为0,类名为DisplaySettings
注释8处调用getIndexableClasss()方法通过反射获取DisplaySettings类
注释9处调用getSearchIndexProvider()方法通过反射获取SEARCH_INDEX_DATA_PROVIDER变量

七.DisplaySettings.java

     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =            new BaseSearchIndexProvider() {                @Override                public List getXmlResourcesToIndex(Context context,                        boolean enabled) {                    ArrayList result =                            new ArrayList();                    SearchIndexableResource sir = new SearchIndexableResource(context);                    sir.xmlResId = R.xml.display_settings;//1                    result.add(sir);                    return result;                }                @Override                public List getNonIndexableKeys(Context context) {                    ArrayList result = new ArrayList();                    if (!context.getResources().getBoolean(                            com.android.internal.R.bool.config_dreamsSupported)) {                        result.add(KEY_SCREEN_SAVER);                    }                    if (!isAutomaticBrightnessAvailable(context.getResources())) {                        result.add(KEY_AUTO_BRIGHTNESS);                    }                    if (!isLiftToWakeAvailable(context)) {                        result.add(KEY_LIFT_TO_WAKE);                    }                    if (!isDozeAvailable(context)) {                        result.add(KEY_DOZE);                    }                    if (!RotationPolicy.isRotationLockToggleVisible(context)) {                        result.add(KEY_AUTO_ROTATE);                    }                    if (!isTapToWakeAvailable(context.getResources())) {                        result.add(KEY_TAP_TO_WAKE);                    }                    /* Sprd: for bug 510399 @{ */                    if (!mHasTouchLight) {                        result.add(KEY_TOUCH_LIGHT);                    }                    /* @} */                    return result;                }            };

display_settings.xml

注释1的地方将R.xml.display_settings赋给了sir.xmlResId,
注释2的地方就是移除掉就可以完成第二项修改,将投射作为内容的地方移除。

OVER!

更多相关文章

  1. mybatisplus的坑 insert标签insert into select无参数问题的解决
  2. python起点网月票榜字体反爬案例
  3. 【阿里云镜像】切换阿里巴巴开源镜像站镜像——Debian镜像
  4. Android(安卓)Fragment基础之动态添加Fragment 实现两个Fragment
  5. Android获取本机电话号码的简单方法
  6. Android(安卓)Binder进程间通信-ServiceManager代理对象的获取过
  7. Android获取本机Mac地址及IP地址的方法
  8. SQlite与android 的数据交互 (android 项目心得三)
  9. ReactNative异常:{"errno":-4048,"code":"EPERM","syscall":"

随机推荐

  1. 传统定位和flex定位
  2. css-flex布局
  3. 【CSS入门】理解css中min-width和max-wid
  4. 210323 CSS 盒子模型 字体图标 定位
  5. 万岳教育直播源码,教育app源码,教育系统
  6. 固定定位:模态框
  7. 苹果Mac强大的思维导图工具:SimpleMind
  8. 如何选择好的运维服务商?
  9. 红帽认证培训(红帽认证视频教程、在线课程
  10. 采用XLL封装工作表函数的演示,确保工作表