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