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