Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 36063b92 authored by jackqdyulei's avatar jackqdyulei
Browse files

Refactor MobileDataPreference

1. Remove it and change it to SwitchPrference
2. Build MobileDataDialogFragment to show dialog when needed
3. Create controller for it to decide when to launch the dialog

Bug: 114749736
Test: RunSettingsRoboTests
Change-Id: I02b9662c5829e765f2c71d10ed951d792cf7aaa1
parent 12964a7e
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@
<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"
    android:title="@string/network_settings_title"
    settings:initialExpandedChildrenCount="4">

    <PreferenceScreen
@@ -23,10 +25,11 @@
        android:title="@string/cdma_lte_data_service">
    </PreferenceScreen>

    <com.android.settings.mobilenetwork.MobileDataPreference
    <SwitchPreference
        android:key="mobile_data_enable"
        android:title="@string/mobile_data_settings_title"
        android:summary="@string/mobile_data_settings_summary"/>
        android:summary="@string/mobile_data_settings_summary"
        settings:controller="com.android.settings.mobilenetwork.MobileDataPreferenceController"/>

    <com.android.settingslib.RestrictedSwitchPreference
        android:key="button_roaming_key"
+133 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settings.mobilenetwork;

import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;

import androidx.appcompat.app.AlertDialog;

import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;


/**
 * Dialog Fragment to show dialog for "mobile data"
 *
 * 1. When user want to disable data in single sim case, show dialog to confirm
 * 2. When user want to enable data in multiple sim case, show dialog to confirm to disable other
 * sim
 */
public class MobileDataDialogFragment extends InstrumentedDialogFragment implements
        DialogInterface.OnClickListener {

    public static final int TYPE_DISABLE_DIALOG = 0;
    public static final int TYPE_MULTI_SIM_DIALOG = 1;

    private static final String ARG_DIALOG_TYPE = "dialog_type";
    private static final String ARG_SUB_ID = "subId";

    private SubscriptionManager mSubscriptionManager;
    private int mType;
    private int mSubId;

    public static MobileDataDialogFragment newInstance(int type, int subId) {
        final MobileDataDialogFragment dialogFragment = new MobileDataDialogFragment();

        Bundle args = new Bundle();
        args.putInt(ARG_DIALOG_TYPE, type);
        args.putInt(ARG_SUB_ID, subId);
        dialogFragment.setArguments(args);

        return dialogFragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mSubscriptionManager = getContext().getSystemService(SubscriptionManager.class);
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        final Bundle bundle = getArguments();
        final Context context = getContext();

        mType = bundle.getInt(ARG_DIALOG_TYPE);
        mSubId = bundle.getInt(ARG_SUB_ID);

        switch (mType) {
            case TYPE_DISABLE_DIALOG:
                return new AlertDialog.Builder(context)
                        .setMessage(R.string.data_usage_disable_mobile)
                        .setPositiveButton(android.R.string.ok, this)
                        .setNegativeButton(android.R.string.cancel, null)
                        .create();
            case TYPE_MULTI_SIM_DIALOG:
                final SubscriptionInfo currentSubInfo =
                        mSubscriptionManager.getActiveSubscriptionInfo(mSubId);
                final SubscriptionInfo nextSubInfo =
                        mSubscriptionManager.getDefaultDataSubscriptionInfo();

                final String previousName = (nextSubInfo == null)
                        ? getContext().getResources().getString(
                        R.string.sim_selection_required_pref)
                        : nextSubInfo.getDisplayName().toString();

                return new AlertDialog.Builder(context)
                        .setTitle(R.string.sim_change_data_title)
                        .setMessage(context.getString(R.string.sim_change_data_message,
                                currentSubInfo != null
                                        ? currentSubInfo.getDisplayName()
                                        : "",
                                previousName))
                        .setPositiveButton(android.R.string.ok, this)
                        .setNegativeButton(R.string.cancel, null)
                        .create();
            default:
                throw new IllegalArgumentException("unknown type " + mType);
        }
    }

    @Override
    public int getMetricsCategory() {
        //TODO(b/114749736): add metric id for this fragment
        return 0;
    }

    @Override
    public void onClick(DialogInterface dialog, int which) {
        switch (mType) {
            case TYPE_DISABLE_DIALOG:
                MobileNetworkUtils.setMobileDataEnabled(getContext(), mSubId, false /* enabled */,
                        false /* disableOtherSubscriptions */);
                break;
            case TYPE_MULTI_SIM_DIALOG:
                mSubscriptionManager.setDefaultDataSubId(mSubId);
                MobileNetworkUtils.setMobileDataEnabled(getContext(), mSubId, true /* enabled */,
                        true /* disableOtherSubscriptions */);
                break;
            default:
                throw new IllegalArgumentException("unknown type " + mType);
        }
    }

}
+0 −324
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settings.mobilenetwork;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.Settings.Global;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.Checkable;

import androidx.preference.DialogPreference;
import androidx.preference.PreferenceScreen;
import androidx.preference.PreferenceViewHolder;

import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;

import java.util.List;

/**
 * Customized Preference to enable / disable mobile data.
 * Basically copy of with com.android.settings.CellDataPreference.
 */
public class MobileDataPreference extends DialogPreference implements
        DialogInterface.OnClickListener {

    private static final boolean DBG = false;
    private static final String TAG = "MobileDataPreference";

    public int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
    public boolean mChecked;
    // Whether to show the dialog to ask switching default data subscription.
    // Should be true only when a multi-sim phone only supports data connection on a single phone,
    // and user is enabling data on the non-default phone.
    public boolean mMultiSimDialog;
    private TelephonyManager mTelephonyManager;
    private SubscriptionManager mSubscriptionManager;

    public MobileDataPreference(Context context, AttributeSet attrs) {
        super(context, attrs, com.android.internal.R.attr.switchPreferenceStyle);
    }

    // Must be called to avoid binder leakage.
    void dispose() {
        mListener.setListener(false, mSubId, getContext());
    }

    @Override
    protected void onRestoreInstanceState(Parcelable s) {
        CellDataState state = (CellDataState) s;
        super.onRestoreInstanceState(state.getSuperState());
        mTelephonyManager = TelephonyManager.from(getContext());
        mChecked = state.mChecked;
        mMultiSimDialog = state.mMultiSimDialog;
        if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
            mSubId = state.mSubId;
            setKey(getKey() + mSubId);
        }
        notifyChanged();
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        CellDataState state = new CellDataState(super.onSaveInstanceState());
        state.mChecked = mChecked;
        state.mMultiSimDialog = mMultiSimDialog;
        state.mSubId = mSubId;
        return state;
    }

    @Override
    public void onAttached() {
        super.onAttached();
        mListener.setListener(true, mSubId, getContext());
    }

    @Override
    protected void onPrepareForRemoval() {
        mListener.setListener(false, mSubId, getContext());
        super.onPrepareForRemoval();
    }

    /**
     * Initialize this preference with subId.
     */
    public void initialize(int subId) {
        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
            throw new IllegalArgumentException("MobileDataPreference needs a SubscriptionInfo");
        }
        mSubscriptionManager = SubscriptionManager.from(getContext());
        mTelephonyManager = TelephonyManager.from(getContext());
        if (mSubId != subId) {
            mSubId = subId;
            setKey(getKey() + subId);
        }
        updateChecked();
    }

    private void updateChecked() {
        setChecked(mTelephonyManager.getDataEnabled(mSubId));
    }

    @Override
    public void performClick() {
        if (!isEnabled() || !SubscriptionManager.isValidSubscriptionId(mSubId)) {
            return;
        }
        final SubscriptionInfo currentSir = mSubscriptionManager.getActiveSubscriptionInfo(
                mSubId);
        final SubscriptionInfo nextSir = mSubscriptionManager.getDefaultDataSubscriptionInfo();
        final boolean isMultiSim = (mTelephonyManager.getSimCount() > 1);
        final boolean isMultipleDataOnCapable =
                (mTelephonyManager.getNumberOfModemsWithSimultaneousDataConnections() > 1);
        final boolean isDefaultDataSubscription = (nextSir != null && currentSir != null
                && currentSir.getSubscriptionId() == nextSir.getSubscriptionId());
        if (mChecked) {
            if (!isMultiSim) {
                // disabling data; show confirmation dialog which eventually
                // calls setMobileDataEnabled() once user confirms.
                mMultiSimDialog = false;
                super.performClick();
            } else {
                // Don't show any dialog.
                setMobileDataEnabled(false /* enabled */, false /* disableOtherSubscriptions */);
            }
        } else {
            if (isMultiSim && !isMultipleDataOnCapable && !isDefaultDataSubscription) {
                // enabling data and setting to default; show confirmation dialog which eventually
                // calls setMobileDataEnabled() once user confirms.
                mMultiSimDialog = true;
                super.performClick();
            } else {
                // Don't show any dialog.
                setMobileDataEnabled(true /* enabled */, false /* disableOtherSubscriptions */);
            }
        }
    }

    private void setMobileDataEnabled(boolean enabled, boolean disableOtherSubscriptions) {
        if (DBG) Log.d(TAG, "setMobileDataEnabled(" + enabled + "," + mSubId + ")");

        MetricsLogger.action(getContext(), MetricsEvent.ACTION_MOBILE_NETWORK_MOBILE_DATA_TOGGLE,
                enabled);

        mTelephonyManager.setDataEnabled(mSubId, enabled);

        if (disableOtherSubscriptions) {
            disableDataForOtherSubscriptions(mSubId);
        }

        setChecked(enabled);
    }

    private void setChecked(boolean checked) {
        if (mChecked == checked) return;
        mChecked = checked;
        notifyChanged();
    }

    @Override
    public void onBindViewHolder(PreferenceViewHolder holder) {
        super.onBindViewHolder(holder);
        View checkableView = holder.findViewById(com.android.internal.R.id.switch_widget);
        checkableView.setClickable(false);
        ((Checkable) checkableView).setChecked(mChecked);
    }

    //TODO(b/114749736): move it to preference controller
    protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
        if (mMultiSimDialog) {
            showMultiSimDialog(builder);
        } else {
            showDisableDialog(builder);
        }
    }

    private void showDisableDialog(AlertDialog.Builder builder) {
        builder.setTitle(null)
                .setMessage(R.string.data_usage_disable_mobile)
                .setPositiveButton(android.R.string.ok, this)
                .setNegativeButton(android.R.string.cancel, null);
    }

    private void showMultiSimDialog(AlertDialog.Builder builder) {
        final SubscriptionInfo currentSir = mSubscriptionManager.getActiveSubscriptionInfo(mSubId);
        final SubscriptionInfo nextSir = mSubscriptionManager.getDefaultDataSubscriptionInfo();

        final String previousName = (nextSir == null)
                ? getContext().getResources().getString(R.string.sim_selection_required_pref)
                : nextSir.getDisplayName().toString();

        builder.setTitle(R.string.sim_change_data_title);
        builder.setMessage(getContext().getString(R.string.sim_change_data_message,
                String.valueOf(currentSir != null ? currentSir.getDisplayName() : null),
                previousName));

        builder.setPositiveButton(android.R.string.ok, this);
        builder.setNegativeButton(R.string.cancel, null);
    }

    private void disableDataForOtherSubscriptions(int subId) {
        List<SubscriptionInfo> subInfoList = mSubscriptionManager.getActiveSubscriptionInfoList();
        if (subInfoList != null) {
            for (SubscriptionInfo subInfo : subInfoList) {
                if (subInfo.getSubscriptionId() != subId) {
                    mTelephonyManager.setDataEnabled(subInfo.getSubscriptionId(), false);
                }
            }
        }
    }

    @Override
    public void onClick(DialogInterface dialog, int which) {
        if (which != DialogInterface.BUTTON_POSITIVE) {
            return;
        }
        if (mMultiSimDialog) {
            mSubscriptionManager.setDefaultDataSubId(mSubId);
            setMobileDataEnabled(true /* enabled */, true /* disableOtherSubscriptions */);
        } else {
            // TODO: extend to modify policy enabled flag.
            setMobileDataEnabled(false /* enabled */, false /* disableOtherSubscriptions */);
        }
    }

    private final DataStateListener mListener = new DataStateListener() {
        @Override
        public void onChange(boolean selfChange) {
            updateChecked();
        }
    };

    /**
     * Listener that listens mobile data state change.
     */
    public abstract static class DataStateListener extends ContentObserver {
        public DataStateListener() {
            super(new Handler(Looper.getMainLooper()));
        }

        /**
         * Set / Unset data state listening, specifying subId.
         */
        public void setListener(boolean listening, int subId, Context context) {
            if (listening) {
                Uri uri = Global.getUriFor(Global.MOBILE_DATA);
                if (TelephonyManager.getDefault().getSimCount() != 1) {
                    uri = Global.getUriFor(Global.MOBILE_DATA + subId);
                }
                context.getContentResolver().registerContentObserver(uri, false, this);
            } else {
                context.getContentResolver().unregisterContentObserver(this);
            }
        }
    }

    /**
     * Class that represents state of mobile data state.
     * Used by onSaveInstanceState and onRestoreInstanceState.
     */
    public static class CellDataState extends BaseSavedState {
        public int mSubId;
        public boolean mChecked;
        public boolean mMultiSimDialog;

        public CellDataState(Parcelable base) {
            super(base);
        }

        public CellDataState(Parcel source) {
            super(source);
            mChecked = source.readByte() != 0;
            mMultiSimDialog = source.readByte() != 0;
            mSubId = source.readInt();
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            super.writeToParcel(dest, flags);
            dest.writeByte((byte) (mChecked ? 1 : 0));
            dest.writeByte((byte) (mMultiSimDialog ? 1 : 0));
            dest.writeInt(mSubId);
        }

        public static final Creator<CellDataState> CREATOR = new Creator<CellDataState>() {
            @Override
            public CellDataState createFromParcel(Parcel source) {
                return new CellDataState(source);
            }

            @Override
            public CellDataState[] newArray(int size) {
                return new CellDataState[size];
            }
        };
    }
}
+189 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settings.mobilenetwork;

import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;

import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;

import com.android.settings.core.TogglePreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;

/**
 * Preference controller for "Mobile data"
 */
public class MobileDataPreferenceController extends TogglePreferenceController
        implements LifecycleObserver, OnStart, OnStop {

    private static final String DIALOG_TAG = "MobileDataDialog";

    private SwitchPreference mPreference;
    private TelephonyManager mTelephonyManager;
    private SubscriptionManager mSubscriptionManager;
    private DataContentObserver mDataContentObserver;
    private FragmentManager mFragmentManager;
    private int mSubId;
    @VisibleForTesting
    int mDialogType;
    @VisibleForTesting
    boolean mNeedDialog;

    public MobileDataPreferenceController(Context context, String key) {
        super(context, key);
        mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
        mDataContentObserver = new DataContentObserver(new Handler(Looper.getMainLooper()));
    }

    @Override
    public int getAvailabilityStatus() {
        return mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
                ? AVAILABLE
                : CONDITIONALLY_UNAVAILABLE;
    }

    @Override
    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);
        mPreference = (SwitchPreference) screen.findPreference(getPreferenceKey());
    }

    @Override
    public void onStart() {
        if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
            mDataContentObserver.register(mContext, mSubId);
        }
    }

    @Override
    public void onStop() {
        if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
            mDataContentObserver.unRegister(mContext);
        }
    }

    @Override
    public boolean handlePreferenceTreeClick(Preference preference) {
        if (TextUtils.equals(preference.getKey(), getPreferenceKey())) {
            if (mNeedDialog) {
                showDialog(mDialogType);
            }
            return true;
        }

        return false;
    }

    @Override
    public boolean setChecked(boolean isChecked) {
        mNeedDialog = isDialogNeeded();

        if (!mNeedDialog) {
            // Update data directly if we don't need dialog
            MobileNetworkUtils.setMobileDataEnabled(mContext, mSubId, isChecked, false);
            return true;
        }

        return false;
    }

    @Override
    public boolean isChecked() {
        return mTelephonyManager.isDataEnabled();
    }

    public void init(FragmentManager fragmentManager, int subId) {
        mFragmentManager = fragmentManager;
        mSubId = subId;
        mTelephonyManager = TelephonyManager.from(mContext).createForSubscriptionId(mSubId);
    }

    @VisibleForTesting
    boolean isDialogNeeded() {
        final boolean enableData = !mTelephonyManager.isDataEnabled();
        final SubscriptionInfo currentSir = mSubscriptionManager.getActiveSubscriptionInfo(
                mSubId);
        final SubscriptionInfo nextSir = mSubscriptionManager.getDefaultDataSubscriptionInfo();
        final boolean isMultiSim = (mTelephonyManager.getSimCount() > 1);
        final boolean isMultipleDataOnCapable =
                (mTelephonyManager.getNumberOfModemsWithSimultaneousDataConnections() > 1);
        final boolean isDefaultDataSubscription = (nextSir != null && currentSir != null
                && currentSir.getSubscriptionId() == nextSir.getSubscriptionId());
        if (enableData) {
            if (isMultiSim && !isMultipleDataOnCapable && !isDefaultDataSubscription) {
                mDialogType = MobileDataDialogFragment.TYPE_MULTI_SIM_DIALOG;
                return true;
            }
        } else {
            if (!isMultiSim) {
                mDialogType = MobileDataDialogFragment.TYPE_DISABLE_DIALOG;
                return true;
            }
        }

        return false;
    }

    private void showDialog(int type) {
        final MobileDataDialogFragment dialogFragment = MobileDataDialogFragment.newInstance(type,
                mSubId);
        dialogFragment.show(mFragmentManager, DIALOG_TAG);
    }

    /**
     * Listener that listens mobile data state change.
     */
    public class DataContentObserver extends ContentObserver {

        public DataContentObserver(Handler handler) {
            super(handler);
        }

        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
            updateState(mPreference);
        }

        public void register(Context context, int subId) {
            Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA);
            if (TelephonyManager.getDefault().getSimCount() != 1) {
                uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA + subId);
            }
            context.getContentResolver().registerContentObserver(uri, false, this);

        }

        public void unRegister(Context context) {
            context.getContentResolver().unregisterContentObserver(this);
        }
    }
}
+28 −20

File changed.

Preview size limit exceeded, changes collapsed.

Loading