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.net.NetworkTemplate; 21 import android.os.Parcel; 22 import android.os.Parcelable; 23 import android.telephony.SubscriptionInfo; 24 import android.telephony.SubscriptionManager; 25 import android.telephony.TelephonyManager; 26 import android.util.AttributeSet; 27 import android.util.Log; 28 import android.view.View; 29 import android.widget.Checkable; 30 31 import androidx.annotation.VisibleForTesting; 32 import androidx.appcompat.app.AlertDialog.Builder; 33 import androidx.core.content.res.TypedArrayUtils; 34 import androidx.preference.PreferenceViewHolder; 35 36 import com.android.settings.R; 37 import com.android.settings.network.MobileDataEnabledListener; 38 import com.android.settings.network.ProxySubscriptionManager; 39 import com.android.settings.network.SubscriptionUtil; 40 import com.android.settings.overlay.FeatureFactory; 41 import com.android.settingslib.CustomDialogPreferenceCompat; 42 43 /** 44 * Preference of cellular data control within Data Usage 45 */ 46 public class CellDataPreference extends CustomDialogPreferenceCompat 47 implements TemplatePreference, MobileDataEnabledListener.Client { 48 49 private static final String TAG = "CellDataPreference"; 50 51 public int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 52 public boolean mChecked; 53 public boolean mMultiSimDialog; 54 private MobileDataEnabledListener mDataStateListener; 55 CellDataPreference(Context context, AttributeSet attrs)56 public CellDataPreference(Context context, AttributeSet attrs) { 57 super(context, attrs, TypedArrayUtils.getAttr(context, 58 androidx.preference.R.attr.switchPreferenceStyle, 59 android.R.attr.switchPreferenceStyle)); 60 mDataStateListener = new MobileDataEnabledListener(context, this); 61 } 62 63 @Override onRestoreInstanceState(Parcelable s)64 protected void onRestoreInstanceState(Parcelable s) { 65 final CellDataState state = (CellDataState) s; 66 super.onRestoreInstanceState(state.getSuperState()); 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 final 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 mDataStateListener.start(mSubId); 89 getProxySubscriptionManager() 90 .addActiveSubscriptionsListener(mOnSubscriptionsChangeListener); 91 } 92 93 @Override onDetached()94 public void onDetached() { 95 mDataStateListener.stop(); 96 getProxySubscriptionManager() 97 .removeActiveSubscriptionsListener(mOnSubscriptionsChangeListener); 98 super.onDetached(); 99 } 100 101 @Override setTemplate(NetworkTemplate template, int subId, NetworkServices services)102 public void setTemplate(NetworkTemplate template, int subId, NetworkServices services) { 103 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 104 throw new IllegalArgumentException("CellDataPreference needs a SubscriptionInfo"); 105 } 106 107 getProxySubscriptionManager() 108 .addActiveSubscriptionsListener(mOnSubscriptionsChangeListener); 109 110 if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 111 mSubId = subId; 112 setKey(getKey() + subId); 113 } 114 updateEnabled(); 115 updateChecked(); 116 } 117 118 @VisibleForTesting getProxySubscriptionManager()119 ProxySubscriptionManager getProxySubscriptionManager() { 120 return ProxySubscriptionManager.getInstance(getContext()); 121 } 122 123 @VisibleForTesting getActiveSubscriptionInfo(int subId)124 SubscriptionInfo getActiveSubscriptionInfo(int subId) { 125 return getProxySubscriptionManager().getActiveSubscriptionInfo(subId); 126 } 127 updateChecked()128 private void updateChecked() { 129 setChecked(getContext().getSystemService(TelephonyManager.class).getDataEnabled(mSubId)); 130 } 131 updateEnabled()132 private void updateEnabled() { 133 // If this subscription is not active, for example, SIM card is taken out, we disable 134 // the button. 135 setEnabled(getActiveSubscriptionInfo(mSubId) != null); 136 } 137 138 @Override performClick(View view)139 protected void performClick(View view) { 140 final Context context = getContext(); 141 FeatureFactory.getFactory(context).getMetricsFeatureProvider() 142 .action(context, SettingsEnums.ACTION_CELL_DATA_TOGGLE, !mChecked); 143 final SubscriptionInfo currentSir = getActiveSubscriptionInfo(mSubId); 144 final SubscriptionInfo nextSir = getActiveSubscriptionInfo( 145 SubscriptionManager.getDefaultDataSubscriptionId()); 146 if (mChecked) { 147 setMobileDataEnabled(false); 148 if (nextSir != null && currentSir != null 149 && currentSir.getSubscriptionId() == nextSir.getSubscriptionId()) { 150 disableDataForOtherSubscriptions(mSubId); 151 } 152 } else { 153 setMobileDataEnabled(true); 154 } 155 } 156 setMobileDataEnabled(boolean enabled)157 private void setMobileDataEnabled(boolean enabled) { 158 if (DataUsageSummary.LOGD) Log.d(TAG, "setMobileDataEnabled(" + enabled + "," 159 + mSubId + ")"); 160 getContext().getSystemService(TelephonyManager.class).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 final View switchView = holder.findViewById(android.R.id.switch_widget); 174 switchView.setClickable(false); 175 ((Checkable) switchView).setChecked(mChecked); 176 } 177 178 @Override onPrepareDialogBuilder(Builder builder, DialogInterface.OnClickListener listener)179 protected void onPrepareDialogBuilder(Builder builder, 180 DialogInterface.OnClickListener listener) { 181 if (mMultiSimDialog) { 182 showMultiSimDialog(builder, listener); 183 } else { 184 showDisableDialog(builder, listener); 185 } 186 } 187 showDisableDialog(Builder builder, DialogInterface.OnClickListener listener)188 private void showDisableDialog(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(Builder builder, DialogInterface.OnClickListener listener)196 private void showMultiSimDialog(Builder builder, 197 DialogInterface.OnClickListener listener) { 198 final SubscriptionInfo currentSir = getActiveSubscriptionInfo(mSubId); 199 final SubscriptionInfo nextSir = getActiveSubscriptionInfo( 200 SubscriptionManager.getDefaultDataSubscriptionId()); 201 202 final String previousName = (nextSir == null) 203 ? getContext().getResources().getString(R.string.sim_selection_required_pref) 204 : SubscriptionUtil.getUniqueSubscriptionDisplayName( 205 nextSir, getContext()).toString(); 206 207 builder.setTitle(R.string.sim_change_data_title); 208 builder.setMessage(getContext().getString(R.string.sim_change_data_message, 209 String.valueOf(currentSir != null 210 ? SubscriptionUtil.getUniqueSubscriptionDisplayName(currentSir, getContext()) 211 : null), previousName)); 212 213 builder.setPositiveButton(R.string.okay, listener); 214 builder.setNegativeButton(R.string.cancel, null); 215 } 216 disableDataForOtherSubscriptions(int subId)217 private void disableDataForOtherSubscriptions(int subId) { 218 final SubscriptionInfo subInfo = getActiveSubscriptionInfo(subId); 219 if (subInfo != null) { 220 getContext().getSystemService(TelephonyManager.class).setDataEnabled(subId, false); 221 } 222 } 223 224 @Override onClick(DialogInterface dialog, int which)225 protected void onClick(DialogInterface dialog, int which) { 226 if (which != DialogInterface.BUTTON_POSITIVE) { 227 return; 228 } 229 if (mMultiSimDialog) { 230 getProxySubscriptionManager().get().setDefaultDataSubId(mSubId); 231 setMobileDataEnabled(true); 232 disableDataForOtherSubscriptions(mSubId); 233 } else { 234 // TODO: extend to modify policy enabled flag. 235 setMobileDataEnabled(false); 236 } 237 } 238 239 @VisibleForTesting 240 final ProxySubscriptionManager.OnActiveSubscriptionChangedListener 241 mOnSubscriptionsChangeListener = 242 new ProxySubscriptionManager.OnActiveSubscriptionChangedListener() { 243 public void onChanged() { 244 if (DataUsageSummary.LOGD) { 245 Log.d(TAG, "onSubscriptionsChanged"); 246 } 247 updateEnabled(); 248 } 249 }; 250 251 /** 252 * Implementation of {@code MobileDataEnabledListener.Client} 253 */ 254 @VisibleForTesting onMobileDataEnabledChange()255 public void onMobileDataEnabledChange() { 256 updateChecked(); 257 } 258 259 public static class CellDataState extends BaseSavedState { 260 public int mSubId; 261 public boolean mChecked; 262 public boolean mMultiSimDialog; 263 CellDataState(Parcelable base)264 public CellDataState(Parcelable base) { 265 super(base); 266 } 267 CellDataState(Parcel source)268 public CellDataState(Parcel source) { 269 super(source); 270 mChecked = source.readByte() != 0; 271 mMultiSimDialog = source.readByte() != 0; 272 mSubId = source.readInt(); 273 } 274 275 @Override writeToParcel(Parcel dest, int flags)276 public void writeToParcel(Parcel dest, int flags) { 277 super.writeToParcel(dest, flags); 278 dest.writeByte((byte) (mChecked ? 1 : 0)); 279 dest.writeByte((byte) (mMultiSimDialog ? 1 : 0)); 280 dest.writeInt(mSubId); 281 } 282 283 public static final Creator<CellDataState> CREATOR = new Creator<CellDataState>() { 284 @Override 285 public CellDataState createFromParcel(Parcel source) { 286 return new CellDataState(source); 287 } 288 289 @Override 290 public CellDataState[] newArray(int size) { 291 return new CellDataState[size]; 292 } 293 }; 294 } 295 } 296