前言
之前写过一篇6.0Settings配置项动态添加和静态添加,已经是很久以前了,
从8.1后Settings加载方式进行了较大改动,到了10.0又和8.1不大一样了,最近刚好又用到这个功能,那就整理分享下。
效果图
一级菜单
二级菜单
文件清单
vendor/mediatek/proprietary/packages/apps/MtkSettings/res/xml/top_level_settings.xml vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/dashboard/DashboardFragment.java vendor/mediatek/proprietary/packages/apps/MtkSettings/res/xml/network_and_internet.xml vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/ethernet/EthernetSettings.java
添加一级菜单
设置主界面对应布局文件为 top_level_settings.xml,里面默认15个 Preference,对应进入设置界面中从上到下
网络和互联网、已连接设备、应用和通知、电池、显示… 也就是说我们在top_level_settings.xml中copy一个
Preference应该就能在主界面显示。
vendor/mediatek/proprietary/packages/apps/MtkSettings/res/xml/top_level_settings.xml
<Preference android:key="top_level_apps_and_notifs" android:title="@string/app_and_notification_dashboard_title" android:summary="@string/app_and_notification_dashboard_summary" android:icon="@drawable/ic_homepage_apps" android:order="-100" android:fragment="com.android.settings.applications.AppAndNotificationDashboardFragment"/> <Preference android:key="level_network" android:title="Preference title" android:summary="Preference summary" android:icon="@drawable/ic_homepage_network" android:order="-95"/>
编译替换后发现真的可以,通过设置 order 大小可以调整设置项位置,order绝对值越大越靠上。
如何指定Preference点击后跳转页面呢?
方式一
此种方式比较简单,直接给 Preference 指定 Intent 属性,这里又可以分为 action 和 targetPackage+targetClass 两种方式
<Preference android:key="level_network" android:title="Preference title" android:summary="Preference summary" android:icon="@drawable/ic_homepage_network" android:order="-95"> <intent android:action="com.test.settings.first"/> </Preference> <Preference android:key="level_network" android:title="Preference title" android:summary="Preference summary" android:icon="@drawable/ic_homepage_network" android:order="-95"> <intent android:targetPackage="com.android.launcher3" android:targetClass="com.android.launcher3.Launcher" /> </Preference>
方式二
略繁琐,找到处理Preference点击逻辑java代码, DashboardFragment 类,这是所有配置项的父类,
点击事件通过 onPreferenceTreeClick() 传递给对应的 AbstractPreferenceController,所以我们可以在
此处拦截处理 level_network 点击事件
vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/dashboard/DashboardFragment.java
@Override public boolean onPreferenceTreeClick(Preference preference) { Collection<List<AbstractPreferenceController>> controllers = mPreferenceControllers.values(); // If preference contains intent, log it before handling. mMetricsFeatureProvider.logDashboardStartIntent( getContext(), preference.getIntent(), getMetricsCategory()); //cczheng add for customerpreference click Log.e(TAG,"keyName="+preference.getKey()); Log.i(TAG,"size="+controllers.size()); if(preference.getKey().equals("level_network")){ Log.e(TAG, "click customer preference...."); try{ getContext().startActivity(new android.content.Intent("com.test.settings.first")); }catch (Exception e){ android.widget.Toast.makeText(getContext(), "app don't esxit", android.widget.Toast.LENGTH_SHORT).show(); } return true; }//E // Give all controllers a chance to handle click. for (List<AbstractPreferenceController> controllerList : controllers) { for (AbstractPreferenceController controller : controllerList) { if (controller.handlePreferenceTreeClick(preference)) { return true; } } } return super.onPreferenceTreeClick(preference); }
ok,这样客户只需要在自己 app 的 AndroidManifest.xml 中配置属性给要跳转的Activity即可
<intent-filter> <action android:name="com.test.settings.first" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter>
优化体验
当客户的app不存在时,设置中是没必要添加定制Preference,虽然已经加了容错,异常时 Toast 提示,根据app是否安装来动态显示
这样更合理一些,那么应该怎么做呢?
还是刚刚 DashboardFragment 中,通过判断客户app包名是否存在,不存在则remove Preference。
这里说下为什么是在 DashboardFragment 中,xml 是在 TopLevelSettings 中通过 getPreferenceScreenResId() 加载的,
这是个抽象方法,继承父类 DashboardFragment,在 TopLevelSettings 中获取不到 PreferenceScreen 对象,无法操作
vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/dashboard/DashboardFragment.java
private void displayResourceTiles() { final int resId = getPreferenceScreenResId(); if (resId <= 0) { return; } addPreferencesFromResource(resId); final PreferenceScreen screen = getPreferenceScreen(); screen.setOnExpandButtonClickListener(this); mPreferenceControllers.values().stream().flatMap(Collection::stream).forEach( controller -> controller.displayPreference(screen)); //cczheng add check customer app exist final Preference preference = findPreference("level_network"); if (preference != null) { if (!checkTilePackage("com.tencent.music")) { screen.removePreference(preference); } }//E } private boolean checkTilePackage(String packageName){ try { android.content.pm.PackageManager pm = getContext().getPackageManager(); pm.getApplicationInfo(packageName, android.content.pm.PackageManager.GET_UNINSTALLED_PACKAGES); android.util.Log.e("DashboardAdapter", packageName + " app exists show dashboard"); return true; }catch (Exception e){ android.util.Log.e("DashboardAdapter", packageName + " app don't exists"); return false; } }
添加二级菜单
这里以添加以太网设置项为例,每个一级设置项对应的布局几乎都是xml文件,找到对应加载xml直接增加即可
vendor/mediatek/proprietary/packages/apps/MtkSettings/res/xml/network_and_internet.xml
<com.android.settingslib.RestrictedPreference android:key="mobile_network_settings" android:title="@string/network_settings_title" android:summary="@string/summary_placeholder" android:icon="@drawable/ic_network_cell" android:order="-15" settings:keywords="@string/keywords_more_mobile_networks" settings:userRestriction="no_config_mobile_networks" settings:useAdminDisabledSummary="true"> </com.android.settingslib.RestrictedPreference> <!-- cczheng add for ethernet --> <com.android.settingslib.RestrictedPreference android:key="ethernet_settings" android:title="@string/ethernet_settings_title" android:summary="@string/summary_placeholder" android:icon="@drawable/ic_ethernet_cell" android:fragment="com.android.settings.ethernet.EthernetSettings" android:order="-17"/>
通过指定 fragment 属性跳转对应页面,新增对应跳转实现类ok
总结
静态方式增加配置项耦合性较强,APP Action 或包名都需要配置到 Settings 源码中,不灵活。
下篇介绍通过动态方式添加配置项