Home | History | Annotate | Download | only in datausage
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
      5  * except in compliance with the License. You may obtain a copy of the License at
      6  *
      7  *      http://www.apache.org/licenses/LICENSE-2.0
      8  *
      9  * Unless required by applicable law or agreed to in writing, software distributed under the
     10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     11  * KIND, either express or implied. See the License for the specific language governing
     12  * permissions and limitations under the License.
     13  */
     14 
     15 package com.android.settings.datausage;
     16 
     17 import android.app.AlertDialog;
     18 import android.content.Context;
     19 import android.content.DialogInterface;
     20 import android.database.ContentObserver;
     21 import android.net.NetworkTemplate;
     22 import android.net.Uri;
     23 import android.os.Handler;
     24 import android.os.Looper;
     25 import android.os.Parcel;
     26 import android.os.Parcelable;
     27 import android.provider.Settings.Global;
     28 import android.support.annotation.VisibleForTesting;
     29 import android.support.v4.content.res.TypedArrayUtils;
     30 import android.support.v7.preference.PreferenceViewHolder;
     31 import android.telephony.SubscriptionInfo;
     32 import android.telephony.SubscriptionManager;
     33 import android.telephony.TelephonyManager;
     34 import android.util.AttributeSet;
     35 import android.util.Log;
     36 import android.view.View;
     37 import android.widget.Checkable;
     38 
     39 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
     40 import com.android.settings.R;
     41 import com.android.settings.Utils;
     42 import com.android.settings.overlay.FeatureFactory;
     43 import com.android.settingslib.CustomDialogPreference;
     44 
     45 import java.util.List;
     46 
     47 public class CellDataPreference extends CustomDialogPreference implements TemplatePreference {
     48 
     49     private static final String TAG = "CellDataPreference";
     50 
     51     public int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     52     public boolean mChecked;
     53     public boolean mMultiSimDialog;
     54     private TelephonyManager mTelephonyManager;
     55     @VisibleForTesting
     56     SubscriptionManager mSubscriptionManager;
     57 
     58     public CellDataPreference(Context context, AttributeSet attrs) {
     59         super(context, attrs, TypedArrayUtils.getAttr(context,
     60                 android.support.v7.preference.R.attr.switchPreferenceStyle,
     61                 android.R.attr.switchPreferenceStyle));
     62     }
     63 
     64     @Override
     65     protected void onRestoreInstanceState(Parcelable s) {
     66         CellDataState state = (CellDataState) s;
     67         super.onRestoreInstanceState(state.getSuperState());
     68         mTelephonyManager = TelephonyManager.from(getContext());
     69         mChecked = state.mChecked;
     70         mMultiSimDialog = state.mMultiSimDialog;
     71         if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
     72             mSubId = state.mSubId;
     73             setKey(getKey() + mSubId);
     74         }
     75         notifyChanged();
     76     }
     77 
     78     @Override
     79     protected Parcelable onSaveInstanceState() {
     80         CellDataState state = new CellDataState(super.onSaveInstanceState());
     81         state.mChecked = mChecked;
     82         state.mMultiSimDialog = mMultiSimDialog;
     83         state.mSubId = mSubId;
     84         return state;
     85     }
     86 
     87     @Override
     88     public void onAttached() {
     89         super.onAttached();
     90         mDataStateListener.setListener(true, mSubId, getContext());
     91         if (mSubscriptionManager!= null) {
     92             mSubscriptionManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener);
     93         }
     94     }
     95 
     96     @Override
     97     public void onDetached() {
     98         mDataStateListener.setListener(false, mSubId, getContext());
     99         if (mSubscriptionManager!= null) {
    100             mSubscriptionManager.removeOnSubscriptionsChangedListener(
    101                     mOnSubscriptionsChangeListener);
    102         }
    103         super.onDetached();
    104     }
    105 
    106     @Override
    107     public void setTemplate(NetworkTemplate template, int subId, NetworkServices services) {
    108         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
    109             throw new IllegalArgumentException("CellDataPreference needs a SubscriptionInfo");
    110         }
    111         mSubscriptionManager = SubscriptionManager.from(getContext());
    112         mTelephonyManager = TelephonyManager.from(getContext());
    113 
    114         mSubscriptionManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener);
    115 
    116         if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
    117             mSubId = subId;
    118             setKey(getKey() + subId);
    119         }
    120         updateEnabled();
    121         updateChecked();
    122     }
    123 
    124     private void updateChecked() {
    125         setChecked(mTelephonyManager.getDataEnabled(mSubId));
    126     }
    127 
    128     private void updateEnabled() {
    129         // If this subscription is not active, for example, SIM card is taken out, we disable
    130         // the button.
    131         setEnabled(mSubscriptionManager.getActiveSubscriptionInfo(mSubId) != null);
    132     }
    133 
    134     @Override
    135     protected void performClick(View view) {
    136         final Context context = getContext();
    137         FeatureFactory.getFactory(context).getMetricsFeatureProvider()
    138                 .action(context, MetricsEvent.ACTION_CELL_DATA_TOGGLE, !mChecked);
    139         final SubscriptionInfo currentSir = mSubscriptionManager.getActiveSubscriptionInfo(
    140                 mSubId);
    141         final SubscriptionInfo nextSir = mSubscriptionManager.getDefaultDataSubscriptionInfo();
    142         if (mChecked) {
    143             // If the device is single SIM or is enabling data on the active data SIM then forgo
    144             // the pop-up.
    145             if (!Utils.showSimCardTile(getContext()) ||
    146                     (nextSir != null && currentSir != null &&
    147                             currentSir.getSubscriptionId() == nextSir.getSubscriptionId())) {
    148                 setMobileDataEnabled(false);
    149                 if (nextSir != null && currentSir != null &&
    150                         currentSir.getSubscriptionId() == nextSir.getSubscriptionId()) {
    151                     disableDataForOtherSubscriptions(mSubId);
    152                 }
    153                 return;
    154             }
    155             // disabling data; show confirmation dialog which eventually
    156             // calls setMobileDataEnabled() once user confirms.
    157             mMultiSimDialog = false;
    158             super.performClick(view);
    159         } else {
    160             // If we are showing the Sim Card tile then we are a Multi-Sim device.
    161             if (Utils.showSimCardTile(getContext())) {
    162                 mMultiSimDialog = true;
    163                 if (nextSir != null && currentSir != null &&
    164                         currentSir.getSubscriptionId() == nextSir.getSubscriptionId()) {
    165                     setMobileDataEnabled(true);
    166                     disableDataForOtherSubscriptions(mSubId);
    167                     return;
    168                 }
    169                 super.performClick(view);
    170             } else {
    171                 setMobileDataEnabled(true);
    172             }
    173         }
    174     }
    175 
    176     private void setMobileDataEnabled(boolean enabled) {
    177         if (DataUsageSummary.LOGD) Log.d(TAG, "setMobileDataEnabled(" + enabled + ","
    178                 + mSubId + ")");
    179         mTelephonyManager.setDataEnabled(mSubId, enabled);
    180         setChecked(enabled);
    181     }
    182 
    183     private void setChecked(boolean checked) {
    184         if (mChecked == checked) return;
    185         mChecked = checked;
    186         notifyChanged();
    187     }
    188 
    189     @Override
    190     public void onBindViewHolder(PreferenceViewHolder holder) {
    191         super.onBindViewHolder(holder);
    192         View switchView = holder.findViewById(android.R.id.switch_widget);
    193         switchView.setClickable(false);
    194         ((Checkable) switchView).setChecked(mChecked);
    195     }
    196 
    197     @Override
    198     protected void onPrepareDialogBuilder(AlertDialog.Builder builder,
    199             DialogInterface.OnClickListener listener) {
    200         if (mMultiSimDialog) {
    201             showMultiSimDialog(builder, listener);
    202         } else {
    203             showDisableDialog(builder, listener);
    204         }
    205     }
    206 
    207     private void showDisableDialog(AlertDialog.Builder builder,
    208             DialogInterface.OnClickListener listener) {
    209         builder.setTitle(null)
    210                 .setMessage(R.string.data_usage_disable_mobile)
    211                 .setPositiveButton(android.R.string.ok, listener)
    212                 .setNegativeButton(android.R.string.cancel, null);
    213     }
    214 
    215     private void showMultiSimDialog(AlertDialog.Builder builder,
    216             DialogInterface.OnClickListener listener) {
    217         final SubscriptionInfo currentSir = mSubscriptionManager.getActiveSubscriptionInfo(mSubId);
    218         final SubscriptionInfo nextSir = mSubscriptionManager.getDefaultDataSubscriptionInfo();
    219 
    220         final String previousName = (nextSir == null)
    221             ? getContext().getResources().getString(R.string.sim_selection_required_pref)
    222             : nextSir.getDisplayName().toString();
    223 
    224         builder.setTitle(R.string.sim_change_data_title);
    225         builder.setMessage(getContext().getString(R.string.sim_change_data_message,
    226                 String.valueOf(currentSir != null ? currentSir.getDisplayName() : null),
    227                 previousName));
    228 
    229         builder.setPositiveButton(R.string.okay, listener);
    230         builder.setNegativeButton(R.string.cancel, null);
    231     }
    232 
    233     private void disableDataForOtherSubscriptions(int subId) {
    234         List<SubscriptionInfo> subInfoList = mSubscriptionManager.getActiveSubscriptionInfoList();
    235         if (subInfoList != null) {
    236             for (SubscriptionInfo subInfo : subInfoList) {
    237                 if (subInfo.getSubscriptionId() != subId) {
    238                     mTelephonyManager.setDataEnabled(subInfo.getSubscriptionId(), false);
    239                 }
    240             }
    241         }
    242     }
    243 
    244     @Override
    245     protected void onClick(DialogInterface dialog, int which) {
    246         if (which != DialogInterface.BUTTON_POSITIVE) {
    247             return;
    248         }
    249         if (mMultiSimDialog) {
    250             mSubscriptionManager.setDefaultDataSubId(mSubId);
    251             setMobileDataEnabled(true);
    252             disableDataForOtherSubscriptions(mSubId);
    253         } else {
    254             // TODO: extend to modify policy enabled flag.
    255             setMobileDataEnabled(false);
    256         }
    257     }
    258 
    259     @VisibleForTesting
    260     final SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangeListener
    261             = new SubscriptionManager.OnSubscriptionsChangedListener() {
    262         @Override
    263         public void onSubscriptionsChanged() {
    264             if (DataUsageSummary.LOGD) {
    265                 Log.d(TAG, "onSubscriptionsChanged");
    266             }
    267             updateEnabled();
    268         }
    269     };
    270 
    271     private final DataStateListener mDataStateListener = new DataStateListener() {
    272         @Override
    273         public void onChange(boolean selfChange) {
    274             updateChecked();
    275         }
    276     };
    277 
    278     public abstract static class DataStateListener extends ContentObserver {
    279         public DataStateListener() {
    280             super(new Handler(Looper.getMainLooper()));
    281         }
    282 
    283         public void setListener(boolean listening, int subId, Context context) {
    284             if (listening) {
    285                 Uri uri = Global.getUriFor(Global.MOBILE_DATA);
    286                 if (TelephonyManager.getDefault().getSimCount() != 1) {
    287                     uri = Global.getUriFor(Global.MOBILE_DATA + subId);
    288                 }
    289                 context.getContentResolver().registerContentObserver(uri, false, this);
    290             } else {
    291                 context.getContentResolver().unregisterContentObserver(this);
    292             }
    293         }
    294     }
    295 
    296     public static class CellDataState extends BaseSavedState {
    297         public int mSubId;
    298         public boolean mChecked;
    299         public boolean mMultiSimDialog;
    300 
    301         public CellDataState(Parcelable base) {
    302             super(base);
    303         }
    304 
    305         public CellDataState(Parcel source) {
    306             super(source);
    307             mChecked = source.readByte() != 0;
    308             mMultiSimDialog = source.readByte() != 0;
    309             mSubId = source.readInt();
    310         }
    311 
    312         @Override
    313         public void writeToParcel(Parcel dest, int flags) {
    314             super.writeToParcel(dest, flags);
    315             dest.writeByte((byte) (mChecked ? 1 : 0));
    316             dest.writeByte((byte) (mMultiSimDialog ? 1 : 0));
    317             dest.writeInt(mSubId);
    318         }
    319 
    320         public static final Creator<CellDataState> CREATOR = new Creator<CellDataState>() {
    321             @Override
    322             public CellDataState createFromParcel(Parcel source) {
    323                 return new CellDataState(source);
    324             }
    325 
    326             @Override
    327             public CellDataState[] newArray(int size) {
    328                 return new CellDataState[size];
    329             }
    330         };
    331     }
    332 }
    333