简介
Settings应用中,提供多类设置菜单入口,每个菜单内又有各模块功能的实现。
那么各个模块基于Settings 基础的界面Fragment去实现UI,层层按不同业务进行封装继承实现子类:
- DashboardFragment
- SettingsPreferenceFragment
功能设置页中的菜单又是通过Controller去实现业务并进行UI动态更新控制。
代码
基于 Android U 平台的实现进行介绍。
公用基类
DashboardFragment
packages/apps/Settings/src/com/android/settings/dashboard/DashboardFragment.java
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 SettingsPreferenceFragment
implements 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 | 响应注册状态变化进行的操作 |
---|---|
[MobileNetworkSettings API] |
java
package com.android.settings.network.telephony;
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class MobileNetworkSettings extends AbstractMobileNetworkSettings implements
MobileNetworkRepository.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.
*/
@Override
public 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
@Override
protected 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));
}
@Override
public 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
@Override
protected 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
以下仅展示部分功能选项/菜单:
XML
<PreferenceScreen
xmlns: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.SettingsMainSwitchPreference
android:key="use_sim_switch"
android:title="@string/mobile_network_use_sim_on"
settings:controller="com.android.settings.network.telephony.MobileNetworkSwitchController"/>
<PreferenceCategory
android:key="enabled_state_container"
android:title="@string/summary_placeholder"
settings:controller="com.android.settings.network.telephony.DisabledSubscriptionController"
android:layout="@layout/preference_category_no_label">
<!--智能数据切换开关-->
<SwitchPreference
android: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.RestrictedSwitchPreference
android: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"/>
<!--数据流量菜单入口-->
<Preference
android:key="data_usage_summary"
android:title="@string/mobile_data_usage_title"
settings:controller="com.android.settings.network.telephony.DataUsagePreferenceController"/>
<!--网络模式列表选择-->
<ListPreference
android: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目录-->
<PreferenceCategory
android:key="network_operators_category_key"
android:title="@string/network_operator_category"
settings:controller="com.android.settings.network.telephony.NetworkPreferenceCategoryController">
<!--自动选网开关-->
<SwitchPreference
android:key="auto_select_key"
android:title="@string/select_automatically"
settings:controller="com.android.settings.network.telephony.gsm.AutoSelectPreferenceController"/>
<!--如果上述自动选网关闭,那么此手动选网菜单,可跳转到网络列表页-->
<Preference
android: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.RestrictedPreference
android: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>