修改Android10+ 原生系统菜单
原文地址 https://www.codenong.com/cs106574155/
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
通过指定 fragment 属性跳转对应页面,新增对应跳转实现类ok
<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"/>
总结
静态方式增加配置项耦合性较强,APP Action 或包名都需要配置到 Settings 源码中,不灵活。