前言
之前寫過一篇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 源碼中,不靈活。
下篇介紹通過動態方式添加配置項