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


目录
相关文章
|
Shell Android开发
Android系统 init.rc文件详解
Android系统 init.rc文件详解
2038 0
|
Oracle Java 关系型数据库
Oracle jdk 的国内下载镜像
Oracle jdk 的国内下载镜像
55961 0
|
Android开发
【错误记录】Android 可执行权限报错 ( Cannot run program “/data/user/0/cn.e/ffmpeg“: error=13,Permission denied )
【错误记录】Android 可执行权限报错 ( Cannot run program “/data/user/0/cn.e/ffmpeg“: error=13,Permission denied )
2232 0
【错误记录】Android 可执行权限报错 ( Cannot run program “/data/user/0/cn.e/ffmpeg“: error=13,Permission denied )
|
Java 开发工具 Android开发
搭建大型源码阅读环境——使用 OpenGrok
RTFSC 是程序员成长的必修课,营造舒适的环境至关重要。本文介绍了阅读大型源码(如 AOSP)的工具选择,重点推荐了免费开源的 OpenGrok。OpenGrok 提供快速搜索、版本历史查看、语法高亮等功能,适用于特大型项目。文章还详细讲解了 OpenGrok 的安装和配置步骤,帮助读者高效阅读源码。
2499 6
|
Java Android开发 芯片
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
本文介绍了如何将基于全志H713芯片的AOSP Android源码导入Android Studio以解决编译和编码问题,通过操作步骤的详细说明,展示了在Android Studio中利用代码提示和补全功能快速定位并修复编译错误的方法。
1163 0
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
|
XML Java Android开发
ListPreference详解与使用
ListPreference详解与使用
|
XML 存储 测试技术
Android系统 添加动态控制SystemUI状态栏、导航栏和下拉菜单
Android系统 添加动态控制SystemUI状态栏、导航栏和下拉菜单
2960 2
|
存储 物联网 数据库
Android 11 以上 SettingsProvider DatabaseHelper 解析
Android 11 以上 SettingsProvider DatabaseHelper 解析
1305 0
|
Android开发
Android源代码定制:Overlay目录定制|调试Overlay资源是否生效
Android源代码定制:Overlay目录定制|调试Overlay资源是否生效
1488 0