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