简介
Settings应用中,提供多类设置菜单入口,每个菜单内又有各模块功能的实现。
那么各个模块基于Settings 基础的界面Fragment去实现UI,层层按不同业务进行封装继承实现子类:
- DashboardFragment
- SettingsPreferenceFragment
功能设置页中的菜单又是通过Controller去实现业务并进行UI动态更新控制。
代码
基于 Android U 平台的实现进行介绍。
公用基类
DashboardFragment
packages/apps/Settings/src/com/android/settings/dashboard/DashboardFragment.java
package com.android.settings.dashboard;/*** Base fragment for dashboard style UI containing a list of static and dynamic setting items.*/
public abstract class DashboardFragment extends SettingsPreferenceFragmentimplements CategoryListener, Indexable, PreferenceGroup.OnExpandButtonClickListener,BasePreferenceController.UiBlockListener {public static final String CATEGORY = "category";private static final String TAG = "DashboardFragment";private static final long TIMEOUT_MILLIS = 50L;/*** Get a list of {@link AbstractPreferenceController} for this fragment.*/protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {return null;}
模块案例
继承关系:(自上而下,底层是父类,除了MobileNetwork,其他都是抽象类,直到Fragment)
- MobileNetworkSettings --- 移动网络设置
- AbstractMobileNetworkSettings
- RestrictedDashboardFragment
- DashboardFragment
- SettingsPreferenceFragment
- InstrumentedPreferenceFragment -- /com/android/settings/core/
- ObservablePreferenceFragment -- /frameworks/base/packages/SettingsLib
- PreferenceFragmentCompat -- classes.jar/androidx.preference
- Fragment
AbstractMobileNetworkSettings 是一个抽象类,用于提供移动网络设置相关的基本功能和行为。
RestrictedDashboardFragment 是在DashboardFragment的基础上添加了一些限制或额外的安全措施,以限制用户对某些设置或操作的访问或更改。可能会对一些敏感的设置进行限制,例如网络设置、安全设置等。也可能会对某些功能进行权限控制,只允许特定用户访问。
DashboardFragment 是基础的仪表盘样式 UI Fragment,用于显示静态和动态的设置项列表。通常不会加入对设置的限制,而是专注于提供用户友好的设置界面和功能。可能包含一些基本的设置项处理和展示逻辑,以便用户能够轻松地进行配置和操作。
MobileNetworkSettings 移动网络设置(界面逻辑)
packages/apps/Settings/src/com/android/settings/network/telephony/MobileNetworkSettings.java
界面布局在mobile_network_settings.xml
API | Function |
---|---|
onPreferenceTreeClick | 定义子功能pref点击事件(override) |
createPreferenceControllers | 创建子功能pref的控制器(override) |
onAttach | 生命周期,use 各类 Controller(override) |
onSubscriptionDetailChanged | 响应注册状态变化进行的操作 |
package com.android.settings.network.telephony;@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class MobileNetworkSettings extends AbstractMobileNetworkSettings implementsMobileNetworkRepository.MobileNetworkCallback {private static final String LOG_TAG = "NetworkSettings";//处理子菜单点击事件/*** Invoked on each preference click in this hierarchy, overrides* PreferenceActivity's implementation. Used to make sure we track the* preference click events.*/@Overridepublic boolean onPreferenceTreeClick(Preference preference) {if (super.onPreferenceTreeClick(preference)) {return true;}final String key = preference.getKey();if (TextUtils.equals(key, BUTTON_CDMA_SYSTEM_SELECT_KEY)|| TextUtils.equals(key, BUTTON_CDMA_SUBSCRIPTION_KEY)) {if (mTelephonyManager.getEmergencyCallbackMode()) {startActivityForResult(new Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null),REQUEST_CODE_EXIT_ECM);mClickedPrefKey = key;}return true;}//【客制化】可通过else if 定制其他keyt的点击行为return false;}//创建各子菜单/功能选项的Controller@Overrideprotected List<AbstractPreferenceController> createPreferenceControllers(Context context) {if (!SubscriptionUtil.isSimHardwareVisible(context)) {finish();return Arrays.asList();}if (getArguments() == null) {Intent intent = getIntent();if (intent != null) {mSubId = intent.getIntExtra(Settings.EXTRA_SUB_ID,MobileNetworkUtils.getSearchableSubscriptionId(context));Log.d(LOG_TAG, "display subId from intent: " + mSubId);} else {Log.d(LOG_TAG, "intent is null, can not get subId " + mSubId + " from intent.");}} else {mSubId = getArguments().getInt(Settings.EXTRA_SUB_ID,MobileNetworkUtils.getSearchableSubscriptionId(context));Log.d(LOG_TAG, "display subId from getArguments(): " + mSubId);}mMobileNetworkRepository = MobileNetworkRepository.getInstance(context);mExecutor.execute(() -> {mSubscriptionInfoEntity = mMobileNetworkRepository.getSubInfoById(String.valueOf(mSubId));mMobileNetworkInfoEntity =mMobileNetworkRepository.queryMobileNetworkInfoBySubId(String.valueOf(mSubId));});return Arrays.asList(new DataUsageSummaryPreferenceController(getActivity(), getSettingsLifecycle(),this, mSubId),new RoamingPreferenceController(context, KEY_ROAMING_PREF, getSettingsLifecycle(),this, mSubId),new CallsDefaultSubscriptionController(context, KEY_CALLS_PREF,getSettingsLifecycle(), this),new SmsDefaultSubscriptionController(context, KEY_SMS_PREF, getSettingsLifecycle(),this),new MobileDataPreferenceController(context, KEY_MOBILE_DATA_PREF,getSettingsLifecycle(), this, mSubId),new ConvertToEsimPreferenceController(context, KEY_CONVERT_TO_ESIM_PREF,getSettingsLifecycle(), this, mSubId));}@Overridepublic void onAttach(Context context) {super.onAttach(context);if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {Log.d(LOG_TAG, "Invalid subId, get the default subscription to show.");SubscriptionInfo info = SubscriptionUtil.getSubscriptionOrDefault(context, mSubId);if (info == null) {Log.d(LOG_TAG, "Invalid subId request " + mSubId);return;}mSubId = info.getSubscriptionId();Log.d(LOG_TAG, "Show NetworkSettings fragment for subId" + mSubId);}Intent intent = getIntent();if (intent != null) {int updateSubscriptionIndex = intent.getIntExtra(Settings.EXTRA_SUB_ID,SubscriptionManager.INVALID_SUBSCRIPTION_ID);if (updateSubscriptionIndex != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {int oldSubId = mSubId;mSubId = updateSubscriptionIndex;// If the subscription has changed or the new intent does not contain the opt in// action,// remove the old discovery dialog. If the activity is being recreated, we will see// onCreate -> onNewIntent, so the dialog will first be recreated for the old// subscription// and then removed.if (updateSubscriptionIndex != oldSubId|| !MobileNetworkActivity.doesIntentContainOptInAction(intent)) {removeContactDiscoveryDialog(oldSubId);}// evaluate showing the new discovery dialog if this intent contains an action to// show the// opt-in.if (MobileNetworkActivity.doesIntentContainOptInAction(intent)) {showContactDiscoveryDialog();}}}final DataUsageSummaryPreferenceController dataUsageSummaryPreferenceController =use(DataUsageSummaryPreferenceController.class);if (dataUsageSummaryPreferenceController != null) {dataUsageSummaryPreferenceController.init(mSubId);}use(MobileNetworkSwitchController.class).init(mSubId);use(CarrierSettingsVersionPreferenceController.class).init(mSubId);use(BillingCyclePreferenceController.class).init(mSubId);use(MmsMessagePreferenceController.class).init(mSubId);use(AutoDataSwitchPreferenceController.class).init(mSubId);use(DisabledSubscriptionController.class).init(mSubId);use(DeleteSimProfilePreferenceController.class).init(mSubId, this,REQUEST_CODE_DELETE_SUBSCRIPTION);use(DisableSimFooterPreferenceController.class).init(mSubId);use(NrDisabledInDsdsFooterPreferenceController.class).init(mSubId);final MobileDataPreferenceController mobileDataPreferenceController =use(MobileDataPreferenceController.class);if (mobileDataPreferenceController != null) {mobileDataPreferenceController.init(getFragmentManager(), mSubId,mSubscriptionInfoEntity, mMobileNetworkInfoEntity);mobileDataPreferenceController.setWifiPickerTrackerHelper(new WifiPickerTrackerHelper(getSettingsLifecycle(), context,null /* WifiPickerTrackerCallback */));}final RoamingPreferenceController roamingPreferenceController =use(RoamingPreferenceController.class);if (roamingPreferenceController != null) {roamingPreferenceController.init(getFragmentManager(), mSubId,mMobileNetworkInfoEntity);}use(ApnPreferenceController.class).init(mSubId);use(CarrierPreferenceController.class).init(mSubId);use(DataUsagePreferenceController.class).init(mSubId);use(PreferredNetworkModePreferenceController.class).init(mSubId);use(EnabledNetworkModePreferenceController.class).init(mSubId);use(DataServiceSetupPreferenceController.class).init(mSubId);use(Enable2gPreferenceController.class).init(mSubId);use(CarrierWifiTogglePreferenceController.class).init(getLifecycle(), mSubId);final WifiCallingPreferenceController wifiCallingPreferenceController =use(WifiCallingPreferenceController.class).init(mSubId);final OpenNetworkSelectPagePreferenceController openNetworkSelectPagePreferenceController =use(OpenNetworkSelectPagePreferenceController.class).init(mSubId);final AutoSelectPreferenceController autoSelectPreferenceController =use(AutoSelectPreferenceController.class).init(getLifecycle(), mSubId).addListener(openNetworkSelectPagePreferenceController);use(NetworkPreferenceCategoryController.class).init(mSubId).setChildren(Arrays.asList(autoSelectPreferenceController));mCdmaSystemSelectPreferenceController = use(CdmaSystemSelectPreferenceController.class);mCdmaSystemSelectPreferenceController.init(getPreferenceManager(), mSubId);mCdmaSubscriptionPreferenceController = use(CdmaSubscriptionPreferenceController.class);mCdmaSubscriptionPreferenceController.init(getPreferenceManager(), mSubId);final VideoCallingPreferenceController videoCallingPreferenceController =use(VideoCallingPreferenceController.class).init(mSubId);use(CallingPreferenceCategoryController.class).setChildren(Arrays.asList(wifiCallingPreferenceController, videoCallingPreferenceController));use(Enhanced4gLtePreferenceController.class).init(mSubId).addListener(videoCallingPreferenceController);use(Enhanced4gCallingPreferenceController.class).init(mSubId).addListener(videoCallingPreferenceController);use(Enhanced4gAdvancedCallingPreferenceController.class).init(mSubId).addListener(videoCallingPreferenceController);use(ContactDiscoveryPreferenceController.class).init(getParentFragmentManager(), mSubId);use(NrAdvancedCallingPreferenceController.class).init(mSubId);use(TransferEsimPreferenceController.class).init(mSubId, mSubscriptionInfoEntity);final ConvertToEsimPreferenceController convertToEsimPreferenceController =use(ConvertToEsimPreferenceController.class);if (convertToEsimPreferenceController != null) {convertToEsimPreferenceController.init(mSubId, mSubscriptionInfoEntity);}List<AbstractSubscriptionPreferenceController> subscriptionPreferenceControllers =useAll(AbstractSubscriptionPreferenceController.class);for (AbstractSubscriptionPreferenceController controller :subscriptionPreferenceControllers) {controller.init(mSubId);}}private void onSubscriptionDetailChanged() {if (mSubscriptionInfoEntity != null) {/*** Update the title when SIM stats got changed*/final Consumer<Activity> renameTitle = activity -> {if (activity != null && !activity.isFinishing()) {if (activity instanceof SettingsActivity) {((SettingsActivity) activity).setTitle(mSubscriptionInfoEntity.uniqueName);}}};ThreadUtils.postOnMainThread(() -> {renameTitle.accept(getActivity());redrawPreferenceControllers();});}}//界面布局在mobile_network_settings.xml@Overrideprotected int getPreferenceScreenResId() {return R.xml.mobile_network_settings;}}
mobile_network_settings 布局
packages/apps/Settings/res/xml/mobile_network_settings.xml
- MobileNetworkSwitchController SIM卡移动网络总开关逻辑
- use_sim_switch 界面的资源key
- mobile_network_use_sim_on 开关名称title
以下仅展示部分功能选项/菜单:
<PreferenceScreenxmlns:android="http://schemas.android.com/apk/res/android"xmlns:settings="http://schemas.android.com/apk/res-auto"android:key="mobile_network_pref_screen"><com.android.settings.widget.SettingsMainSwitchPreferenceandroid:key="use_sim_switch"android:title="@string/mobile_network_use_sim_on"settings:controller="com.android.settings.network.telephony.MobileNetworkSwitchController"/><PreferenceCategoryandroid:key="enabled_state_container"android:title="@string/summary_placeholder"settings:controller="com.android.settings.network.telephony.DisabledSubscriptionController"android:layout="@layout/preference_category_no_label"><!--智能数据切换开关--><SwitchPreferenceandroid:key="auto_data_switch"android:title="@string/auto_data_switch_title"android:summary="@string/auto_data_switch_summary"settings:controller="com.android.settings.network.telephony.AutoDataSwitchPreferenceController"/><!--数据漫游开关--><com.android.settingslib.RestrictedSwitchPreferenceandroid:key="button_roaming_key"android:title="@string/roaming"android:persistent="false"android:summaryOn="@string/roaming_enable"android:summaryOff="@string/roaming_disable"settings:userRestriction="no_data_roaming"settings:controller="com.android.settings.network.telephony.RoamingPreferenceController"/><!--数据流量菜单入口--><Preferenceandroid:key="data_usage_summary"android:title="@string/mobile_data_usage_title"settings:controller="com.android.settings.network.telephony.DataUsagePreferenceController"/><!--网络模式列表选择--><ListPreferenceandroid:key="enabled_networks_key"android:title="@string/preferred_network_mode_title"android:summary="@string/preferred_network_mode_summary"android:entries="@array/enabled_networks_choices"android:entryValues="@array/enabled_networks_values"android:dialogTitle="@string/preferred_network_mode_dialogtitle"settings:controller="com.android.settings.network.telephony.EnabledNetworkModePreferenceController"/><!--Network目录--><PreferenceCategoryandroid:key="network_operators_category_key"android:title="@string/network_operator_category"settings:controller="com.android.settings.network.telephony.NetworkPreferenceCategoryController"><!--自动选网开关--><SwitchPreferenceandroid:key="auto_select_key"android:title="@string/select_automatically"settings:controller="com.android.settings.network.telephony.gsm.AutoSelectPreferenceController"/><!--如果上述自动选网关闭,那么此手动选网菜单,可跳转到网络列表页--><Preferenceandroid:key="choose_network_key"android:title="@string/choose_network_title"settings:controller="com.android.settings.network.telephony.gsm.OpenNetworkSelectPagePreferenceController"/></PreferenceCategory><!--APN设置入口--><!--We want separate APN setting from reset of settings because we want user to change it with caution--><com.android.settingslib.RestrictedPreferenceandroid:key="telephony_apn_key"android:persistent="false"android:title="@string/mobile_network_apn_title"settings:allowDividerAbove="true"settings:keywords="@string/keywords_access_point_names"settings:controller="com.android.settings.network.telephony.ApnPreferenceController"/></PreferenceCategory></PreferenceScreen>