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