【笔记】Android Settings 应用设置菜单的界面代码介绍

简介

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>
相关推荐
雨白8 小时前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹10 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空11 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭12 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日13 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安13 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑13 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟17 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡18 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi0019 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体