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