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.CompoundButton; 30 31 import androidx.annotation.VisibleForTesting; 32 import androidx.appcompat.app.AlertDialog.Builder; 33 import androidx.preference.PreferenceViewHolder; 34 35 import com.android.settings.R; 36 import com.android.settings.network.MobileDataEnabledListener; 37 import com.android.settings.network.ProxySubscriptionManager; 38 import com.android.settings.network.SubscriptionUtil; 39 import com.android.settings.overlay.FeatureFactory; 40 import com.android.settingslib.CustomDialogPreferenceCompat; 41 42 /** 43 * Preference of cellular data control within Data Usage 44 */ 45 public class CellDataPreference extends CustomDialogPreferenceCompat 46 implements TemplatePreference, MobileDataEnabledListener.Client { 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 final MobileDataEnabledListener mDataStateListener; 54 CellDataPreference(Context context, AttributeSet attrs)55 public CellDataPreference(Context context, AttributeSet attrs) { 56 super(context, attrs, androidx.preference.R.attr.switchPreferenceCompatStyle); 57 mDataStateListener = new MobileDataEnabledListener(context, this); 58 } 59 60 @Override onRestoreInstanceState(Parcelable s)61 protected void onRestoreInstanceState(Parcelable s) { 62 final CellDataState state = (CellDataState) s; 63 super.onRestoreInstanceState(state.getSuperState()); 64 mChecked = state.mChecked; 65 mMultiSimDialog = state.mMultiSimDialog; 66 if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 67 mSubId = state.mSubId; 68 setKey(getKey() + mSubId); 69 } 70 notifyChanged(); 71 } 72 73 @Override onSaveInstanceState()74 protected Parcelable onSaveInstanceState() { 75 final CellDataState state = new CellDataState(super.onSaveInstanceState()); 76 state.mChecked = mChecked; 77 state.mMultiSimDialog = mMultiSimDialog; 78 state.mSubId = mSubId; 79 return state; 80 } 81 82 @Override onAttached()83 public void onAttached() { 84 super.onAttached(); 85 mDataStateListener.start(mSubId); 86 getProxySubscriptionManager() 87 .addActiveSubscriptionsListener(mOnSubscriptionsChangeListener); 88 } 89 90 @Override onDetached()91 public void onDetached() { 92 mDataStateListener.stop(); 93 getProxySubscriptionManager() 94 .removeActiveSubscriptionsListener(mOnSubscriptionsChangeListener); 95 super.onDetached(); 96 } 97 98 @Override setTemplate(NetworkTemplate template, int subId)99 public void setTemplate(NetworkTemplate template, int subId) { 100 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 101 throw new IllegalArgumentException("CellDataPreference needs a SubscriptionInfo"); 102 } 103 104 getProxySubscriptionManager() 105 .addActiveSubscriptionsListener(mOnSubscriptionsChangeListener); 106 107 if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 108 mSubId = subId; 109 setKey(getKey() + subId); 110 } 111 updateEnabled(); 112 updateChecked(); 113 } 114 115 @VisibleForTesting getProxySubscriptionManager()116 ProxySubscriptionManager getProxySubscriptionManager() { 117 return ProxySubscriptionManager.getInstance(getContext()); 118 } 119 120 @VisibleForTesting getActiveSubscriptionInfo(int subId)121 SubscriptionInfo getActiveSubscriptionInfo(int subId) { 122 return getProxySubscriptionManager().getActiveSubscriptionInfo(subId); 123 } 124 updateChecked()125 private void updateChecked() { 126 setChecked(getContext().getSystemService(TelephonyManager.class).getDataEnabled(mSubId)); 127 } 128 updateEnabled()129 private void updateEnabled() { 130 // If this subscription is not active, for example, SIM card is taken out, we disable 131 // the button. 132 setEnabled(getActiveSubscriptionInfo(mSubId) != null); 133 } 134 135 @Override performClick(View view)136 protected void performClick(View view) { 137 final Context context = getContext(); 138 FeatureFactory.getFeatureFactory().getMetricsFeatureProvider() 139 .action(context, SettingsEnums.ACTION_CELL_DATA_TOGGLE, !mChecked); 140 final SubscriptionInfo currentSir = getActiveSubscriptionInfo(mSubId); 141 final SubscriptionInfo nextSir = getActiveSubscriptionInfo( 142 SubscriptionManager.getDefaultDataSubscriptionId()); 143 if (mChecked) { 144 setMobileDataEnabled(false); 145 if (nextSir != null && currentSir != null 146 && currentSir.getSubscriptionId() == nextSir.getSubscriptionId()) { 147 disableDataForOtherSubscriptions(mSubId); 148 } 149 } else { 150 setMobileDataEnabled(true); 151 } 152 } 153 setMobileDataEnabled(boolean enabled)154 private void setMobileDataEnabled(boolean enabled) { 155 Log.d(TAG, "setDataEnabledForReason (" + enabled + "," + mSubId + ")"); 156 getContext().getSystemService(TelephonyManager.class).setDataEnabled(mSubId, enabled); 157 setChecked(enabled); 158 } 159 setChecked(boolean checked)160 private void setChecked(boolean checked) { 161 if (mChecked == checked) return; 162 mChecked = checked; 163 notifyChanged(); 164 } 165 166 @Override onBindViewHolder(PreferenceViewHolder holder)167 public void onBindViewHolder(PreferenceViewHolder holder) { 168 super.onBindViewHolder(holder); 169 final CompoundButton switchView = 170 (CompoundButton) holder.findViewById(androidx.preference.R.id.switchWidget); 171 switchView.setClickable(false); 172 switchView.setChecked(mChecked); 173 } 174 175 @Override onPrepareDialogBuilder(Builder builder, DialogInterface.OnClickListener listener)176 protected void onPrepareDialogBuilder(Builder builder, 177 DialogInterface.OnClickListener listener) { 178 if (mMultiSimDialog) { 179 showMultiSimDialog(builder, listener); 180 } else { 181 showDisableDialog(builder, listener); 182 } 183 } 184 showDisableDialog(Builder builder, DialogInterface.OnClickListener listener)185 private void showDisableDialog(Builder builder, 186 DialogInterface.OnClickListener listener) { 187 builder.setTitle(null) 188 .setMessage(R.string.data_usage_disable_mobile) 189 .setPositiveButton(android.R.string.ok, listener) 190 .setNegativeButton(android.R.string.cancel, null); 191 } 192 showMultiSimDialog(Builder builder, DialogInterface.OnClickListener listener)193 private void showMultiSimDialog(Builder builder, 194 DialogInterface.OnClickListener listener) { 195 final SubscriptionInfo currentSir = getActiveSubscriptionInfo(mSubId); 196 final SubscriptionInfo nextSir = getActiveSubscriptionInfo( 197 SubscriptionManager.getDefaultDataSubscriptionId()); 198 199 final String previousName = (nextSir == null) 200 ? getContext().getResources().getString(R.string.sim_selection_required_pref) 201 : SubscriptionUtil.getUniqueSubscriptionDisplayName( 202 nextSir, getContext()).toString(); 203 204 builder.setTitle(R.string.sim_change_data_title); 205 builder.setMessage(getContext().getString(R.string.sim_change_data_message, 206 String.valueOf(currentSir != null 207 ? SubscriptionUtil.getUniqueSubscriptionDisplayName(currentSir, getContext()) 208 : null), 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 final SubscriptionInfo subInfo = getActiveSubscriptionInfo(subId); 216 if (subInfo != null) { 217 getContext().getSystemService(TelephonyManager.class).setDataEnabled(subId, false); 218 } 219 } 220 221 @Override onClick(DialogInterface dialog, int which)222 protected void onClick(DialogInterface dialog, int which) { 223 if (which != DialogInterface.BUTTON_POSITIVE) { 224 return; 225 } 226 if (mMultiSimDialog) { 227 getProxySubscriptionManager().get().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 @VisibleForTesting 237 final ProxySubscriptionManager.OnActiveSubscriptionChangedListener 238 mOnSubscriptionsChangeListener = 239 new ProxySubscriptionManager.OnActiveSubscriptionChangedListener() { 240 public void onChanged() { 241 if (DataUsageSummary.LOGD) { 242 Log.d(TAG, "onSubscriptionsChanged"); 243 } 244 updateEnabled(); 245 } 246 }; 247 248 /** 249 * Implementation of {@code MobileDataEnabledListener.Client} 250 */ 251 @VisibleForTesting onMobileDataEnabledChange()252 public void onMobileDataEnabledChange() { 253 updateChecked(); 254 } 255 256 public static class CellDataState extends BaseSavedState { 257 public int mSubId; 258 public boolean mChecked; 259 public boolean mMultiSimDialog; 260 CellDataState(Parcelable base)261 public CellDataState(Parcelable base) { 262 super(base); 263 } 264 CellDataState(Parcel source)265 public CellDataState(Parcel source) { 266 super(source); 267 mChecked = source.readByte() != 0; 268 mMultiSimDialog = source.readByte() != 0; 269 mSubId = source.readInt(); 270 } 271 272 @Override writeToParcel(Parcel dest, int flags)273 public void writeToParcel(Parcel dest, int flags) { 274 super.writeToParcel(dest, flags); 275 dest.writeByte((byte) (mChecked ? 1 : 0)); 276 dest.writeByte((byte) (mMultiSimDialog ? 1 : 0)); 277 dest.writeInt(mSubId); 278 } 279 280 public static final Creator<CellDataState> CREATOR = new Creator<CellDataState>() { 281 @Override 282 public CellDataState createFromParcel(Parcel source) { 283 return new CellDataState(source); 284 } 285 286 @Override 287 public CellDataState[] newArray(int size) { 288 return new CellDataState[size]; 289 } 290 }; 291 } 292 } 293