Settings中动态插入菜单

简介: Settings中动态插入菜单

Android设备中,很多应用需要增加在Settings中增加菜单,作为应用的入口。此时可以仿照google GMS包的应用,采用动态加载的方式。这种方法不需要修改Settings中代码,修改应用本身的AndroidManifest.xml文件就行,实现解耦并自动适配

1、使用方法

1.1、示例:

在AndroidMainfest.xml中增加如下配置

 
 
        <!--最后的效果是Settings-Display中会增加一个菜单,点击该菜单进入MainActivity-->
        <activity android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
 
            </intent-filter>
              <!--Settings会检测该action来查找要插入的菜单-->
            <intent-filter android:priority="6">
                <action android:name="com.android.settings.action.EXTRA_SETTINGS" />
            </intent-filter>
 
            <!--确定在哪个界面添加,这是表示在Settings-display中增加,
            具体界面的值可以在CategoryKey.java中查看-->
            <meta-data
                android:name="com.android.settings.category"
                android:value="com.android.settings.category.ia.display" />
 
            <!--菜单标题-->
            <meta-data
                android:name="com.android.settings.title"
                android:value="@string/app_name"/>
 
            <!--菜单summary-->
            <meta-data
                android:name="com.android.settings.summary"
                android:resource="@string/summary_text" />
 
            <!--菜单icon-->
            <meta-data
                android:name="com.android.settings.icon"
                android:resource="@drawable/icon_setings" />
            
            <!--菜单显示位置-->
            <meta-data
                android:name="com.android.settings.order"
                android:value="-100" />
 
        </activity>

1.2、权限

动态插入菜单有一个条件,需要插入菜单的应用必须为system应用,若要所有的应用都能插入菜单,需要修改Settings源码中这段逻辑

frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java

 private static void loadActivityTiles(Context context,
            UserHandle user, Map<Pair<String, String>, Tile> addedCache,
            String defaultCategory, List<Tile> outTiles, Intent intent) {
        final PackageManager pm = context.getPackageManager();
        //获取meta data
        final List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
                PackageManager.GET_META_DATA, user.getIdentifier());
        for (ResolveInfo resolved : results) {
            if (!resolved.system) {   //判断是否为system应用
                // Do not allow any app to add to settings, only system ones.
                //注释此处,则任何应用都可以插入菜单
                //continue;
            }
            final ActivityInfo activityInfo = resolved.activityInfo;
            final Bundle metaData = activityInfo.metaData;
            //加载菜单数据
            loadTile(user, addedCache, defaultCategory, outTiles, intent, metaData, activityInfo);
        }
    }

2、原理

查看Settings源码,分析流程

2.1、数据更新

SettingsActivity.java。Settings中的界面主要为Settings和SubSettings,都需要继承SettingsActivity。故数据更新在该类onResume中
SettingsActivity-onResume
SettingsActivity:
 
@Override
    protected void onResume() {
        ...
        updateTilesList();  //1
}
 
private void updateTilesList() {
        // Generally the items that are will be changing from these updates will
        // not be in the top list of tiles, so run it in the background and the
        // SettingsBaseActivity will pick up on the updates automatically.
        AsyncTask.execute(() -> doUpdateTilesList());   //2
    }
 
private void doUpdateTilesList() {
     // Final step, refresh categories.
        if (somethingChanged) {
            Log.d(LOG_TAG, "Enabled state changed for some tiles, reloading all categories "
                    + changedList.toString());
            mCategoryMixin.updateCategories();    //3
        } else {
            Log.d(LOG_TAG, "No enabled state changed, skipping updateCategory call");
        }
 
}
 
CategoryMixin:
    /**
     * Updates dashboard categories.
     */
    public void updateCategories() {
        updateCategories(false /* fromBroadcast */);   //4
    }
 
private void updateCategories(boolean fromBroadcast) {
        // Only allow at most 2 tasks existing at the same time since when the first one is
        // executing, there may be new data from the second update request.
        // Ignore the third update request because the second task is still waiting for the first
        // task to complete in a serial thread, which will get the latest data.
        if (mCategoriesUpdateTaskCount < 2) {
            new CategoriesUpdateTask().execute(fromBroadcast);   //5
        }
    }
 
CategoryMixin$CategoriesUpdateTask:
 @Override
        protected Set<String> doInBackground(Boolean... params) {
            mPreviousTileMap = mCategoryManager.getTileByComponentMap();
            mCategoryManager.reloadAllCategories(mContext);   //6
            mCategoryManager.updateCategoryFromDenylist(sTileDenylist);
            return getChangedCategories(params[0]);
        }
 
CategoryManager:
public synchronized void reloadAllCategories(Context context) {
        final boolean forceClearCache = mInterestingConfigChanges.applyNewConfig(
                context.getResources());
        mCategories = null;
        tryInitCategories(context, forceClearCache);  //7   forceClearCache用来保存Tile
    }
 
private synchronized void tryInitCategories(Context context, boolean forceClearCache) {
    ...
    mCategories = TileUtils.getCategories(context, mTileByComponentCache);  //8
    ...
}
 
TileUtils:
/**
     * Build a list of DashboardCategory.
     */
    public static List<DashboardCategory> getCategories(Context context,
            Map<Pair<String, String>, Tile> cache) {
        ...
        loadTilesForAction(context, user, SETTINGS_ACTION, cache, null, tiles, true); //9
        ...
}
 static void loadTilesForAction(Context context,
            UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache,
            String defaultCategory, List<Tile> outTiles, boolean requireSettings) {
        final Intent intent = new Intent(action);
        if (requireSettings) {
            intent.setPackage(SETTING_PKG);
        }
//10
        loadActivityTiles(context, user, addedCache, defaultCategory, outTiles, intent); 
        loadProviderTiles(context, user, addedCache, defaultCategory, outTiles, intent);
    }
 private static void loadActivityTiles(Context context,
            UserHandle user, Map<Pair<String, String>, Tile> addedCache,
            String defaultCategory, List<Tile> outTiles, Intent intent) {
        final PackageManager pm = context.getPackageManager();
        final List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
                PackageManager.GET_META_DATA, user.getIdentifier());
        for (ResolveInfo resolved : results) {
            if (!resolved.system) {
                // Do not allow any app to add to settings, only system ones.
                continue;
            }
            final ActivityInfo activityInfo = resolved.activityInfo;
            final Bundle metaData = activityInfo.metaData;
            loadTile(user, addedCache, defaultCategory, outTiles, intent, metaData, activityInfo);   //11
        }
    }
 
private static void loadTile(UserHandle user, Map<Pair<String, String>, Tile> addedCache,
            String defaultCategory, List<Tile> outTiles, Intent intent, Bundle metaData,
            ComponentInfo componentInfo) {}  //12
 


2.2、插入Preference

DashboardFragment:
 @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        checkUiBlocker(mControllers);
        refreshAllPreferences(getLogTag());//1
        ...
}
 
private void refreshAllPreferences(final String tag) {
    refreshDashboardTiles(tag);   //2
}
 
 /**
     * Refresh preference items backed by DashboardCategory.
     */
    private void refreshDashboardTiles(final String tag) {
        final PreferenceScreen screen = getPreferenceScreen();  //获取根布局PreferenceScreen
 
        //3 获取Category
        final DashboardCategory category =
                mDashboardFeatureProvider.getTilesForCategory(getCategoryKey());  
        ...
if (mDashboardTilePrefKeys.containsKey(key)) {
                // Have the key already, will rebind.
                final Preference preference = screen.findPreference(key);
                observers = mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(
                        getActivity(), this, forceRoundedIcons, preference, tile, key,
                        mPlaceholderPreferenceController.getOrder());
            }else {
                // Don't have this key, add it.
                final Preference pref = createPreference(tile);
                observers = mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(
                        getActivity(), this, forceRoundedIcons, pref, tile, key,
                        mPlaceholderPreferenceController.getOrder());
                screen.addPreference(pref);   //4   添加Preference,即插入菜单
                registerDynamicDataObservers(observers);
                mDashboardTilePrefKeys.put(key, observers);
            }


目录
相关文章
|
8月前
|
Java Android开发
Android系统 修改无源码普通应用为默认Launcher和隐藏Settings中应用信息图标
Android系统 修改无源码普通应用为默认Launcher和隐藏Settings中应用信息图标
1020 0
好的学习方法------快捷键汇总页面,ncpa.cpl,设置成一个路由,quick,里面存入快捷键,quick路由放快捷键,link放快捷方式
好的学习方法------快捷键汇总页面,ncpa.cpl,设置成一个路由,quick,里面存入快捷键,quick路由放快捷键,link放快捷方式
|
4月前
|
JavaScript
element-ui table表格多选后再打开默认选中
element-ui table表格多选后再打开默认选中
|
5月前
|
JavaScript 前端开发
Vue实现Element UI框架的自定义输入框或下拉框在输入时对列表选项进行过滤,以及右键列表选项弹出菜单进行删除
本文介绍了如何在Vue框架结合Element UI库实现自定义输入框或下拉框,在输入时对列表选项进行过滤,并支持右键点击列表选项弹出菜单进行删除的功能。
123 0
ElementPlus菜单如何默认打开第一个,router-view里替换变的,menu菜单没有跳转怎么办,开启路由:router=“true“,如何设置点击空格就调用方法
ElementPlus菜单如何默认打开第一个,router-view里替换变的,menu菜单没有跳转怎么办,开启路由:router=“true“,如何设置点击空格就调用方法
ElementPlus菜单如何默认打开第一个,router-view里替换变的,menu菜单没有跳转怎么办,开启路由:router=“true“,如何设置点击空格就调用方法
|
前端开发
Bootstrap tab页签刷新加载不显示,只有点击其他标签后第一个才显示
Bootstrap tab页签刷新加载不显示,只有点击其他标签后第一个才显示
115 0
|
API
ant.design Table组件点击一个选项框却把所有选项都选中了
ant.design Table组件点击一个选项框却把所有选项都选中了
191 0
ant.design Table组件点击一个选项框却把所有选项都选中了
|
JavaScript 开发工具 git
Element-ui中 表格(Table)组件中 toggleRowSelection 方法设置默认多选项 无法选中解决思路
Element-ui中 表格(Table)组件中 toggleRowSelection 方法设置默认多选项 无法选中解决思路
1331 0
Element-ui中 表格(Table)组件中 toggleRowSelection 方法设置默认多选项 无法选中解决思路
|
Web App开发
Bootstrap学习笔记--下拉菜单,折叠,菜单,标签,Pills
下拉菜单: 下拉菜单是比较常见的 直接看源码: .dropdown表示下拉框 按钮和下拉框都要放在dropdown类的div下 要打开下拉菜单,请使用按钮或具有属性的类.
3254 0