• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CellDataPreference(Context context, AttributeSet attrs)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
onRestoreInstanceState(Parcelable s)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
onSaveInstanceState()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
onAttached()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
onDetached()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
setTemplate(NetworkTemplate template, int subId, NetworkServices services)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 
updateChecked()124     private void updateChecked() {
125         setChecked(mTelephonyManager.getDataEnabled(mSubId));
126     }
127 
updateEnabled()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
performClick(View view)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 
setMobileDataEnabled(boolean enabled)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 
setChecked(boolean checked)183     private void setChecked(boolean checked) {
184         if (mChecked == checked) return;
185         mChecked = checked;
186         notifyChanged();
187     }
188 
189     @Override
onBindViewHolder(PreferenceViewHolder holder)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
onPrepareDialogBuilder(AlertDialog.Builder builder, DialogInterface.OnClickListener listener)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 
showDisableDialog(AlertDialog.Builder builder, DialogInterface.OnClickListener listener)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 
showMultiSimDialog(AlertDialog.Builder builder, DialogInterface.OnClickListener listener)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 
disableDataForOtherSubscriptions(int subId)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
onClick(DialogInterface dialog, int which)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 {
DataStateListener()279         public DataStateListener() {
280             super(new Handler(Looper.getMainLooper()));
281         }
282 
setListener(boolean listening, int subId, Context context)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 
CellDataState(Parcelable base)301         public CellDataState(Parcelable base) {
302             super(base);
303         }
304 
CellDataState(Parcel source)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
writeToParcel(Parcel dest, int flags)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