CellularTile.java revision 53f3f15654a12a820e08cd5d42b5f94d061598ab
1/* 2 * Copyright (C) 2014 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.systemui.qs.tiles; 18 19import android.app.AlertDialog; 20import android.app.AlertDialog.Builder; 21import android.content.ComponentName; 22import android.content.Context; 23import android.content.Intent; 24import android.content.pm.PackageManager; 25import android.content.res.Resources; 26import android.os.SystemProperties; 27import android.provider.Settings; 28import android.service.quicksettings.Tile; 29import android.view.LayoutInflater; 30import android.view.View; 31import android.view.ViewGroup; 32import android.view.WindowManager.LayoutParams; 33import android.widget.Switch; 34import com.android.internal.logging.MetricsLogger; 35import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 36import com.android.settingslib.net.DataUsageController; 37import com.android.systemui.Dependency; 38import com.android.systemui.R; 39import com.android.systemui.R.string; 40import com.android.systemui.plugins.ActivityStarter; 41import com.android.systemui.plugins.qs.DetailAdapter; 42import com.android.systemui.plugins.qs.QSIconView; 43import com.android.systemui.plugins.qs.QSTile.SignalState; 44import com.android.systemui.qs.CellTileView; 45import com.android.systemui.qs.CellTileView.SignalIcon; 46import com.android.systemui.qs.QSHost; 47import com.android.systemui.qs.tileimpl.QSTileImpl; 48import com.android.systemui.statusbar.phone.SystemUIDialog; 49import com.android.systemui.statusbar.policy.KeyguardMonitor; 50import com.android.systemui.statusbar.policy.NetworkController; 51import com.android.systemui.statusbar.policy.NetworkController.IconState; 52import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; 53 54/** Quick settings tile: Cellular **/ 55public class CellularTile extends QSTileImpl<SignalState> { 56 private static final String ENABLE_SETTINGS_DATA_PLAN = "enable.settings.data.plan"; 57 58 private final NetworkController mController; 59 private final DataUsageController mDataController; 60 private final CellularDetailAdapter mDetailAdapter; 61 62 private final CellSignalCallback mSignalCallback = new CellSignalCallback(); 63 private final ActivityStarter mActivityStarter; 64 private final KeyguardMonitor mKeyguardMonitor; 65 66 public CellularTile(QSHost host) { 67 super(host); 68 mController = Dependency.get(NetworkController.class); 69 mActivityStarter = Dependency.get(ActivityStarter.class); 70 mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); 71 mDataController = mController.getMobileDataController(); 72 mDetailAdapter = new CellularDetailAdapter(); 73 } 74 75 @Override 76 public SignalState newTileState() { 77 return new SignalState(); 78 } 79 80 @Override 81 public DetailAdapter getDetailAdapter() { 82 return mDetailAdapter; 83 } 84 85 @Override 86 public void handleSetListening(boolean listening) { 87 if (listening) { 88 mController.addCallback(mSignalCallback); 89 } else { 90 mController.removeCallback(mSignalCallback); 91 } 92 } 93 94 @Override 95 public QSIconView createTileView(Context context) { 96 return new CellTileView(context); 97 } 98 99 @Override 100 public Intent getLongClickIntent() { 101 return getCellularSettingIntent(); 102 } 103 104 @Override 105 protected void handleClick() { 106 if (getState().state == Tile.STATE_UNAVAILABLE) { 107 return; 108 } 109 if (mDataController.isMobileDataEnabled()) { 110 if (mKeyguardMonitor.isSecure() && !mKeyguardMonitor.canSkipBouncer()) { 111 mActivityStarter.postQSRunnableDismissingKeyguard(this::showDisableDialog); 112 } else { 113 mUiHandler.post(this::showDisableDialog); 114 } 115 } else { 116 mDataController.setMobileDataEnabled(true); 117 } 118 } 119 120 private void showDisableDialog() { 121 mHost.collapsePanels(); 122 AlertDialog dialog = new Builder(mContext) 123 .setMessage(string.data_usage_disable_mobile) 124 .setNegativeButton(android.R.string.cancel, null) 125 .setPositiveButton( 126 com.android.internal.R.string.alert_windows_notification_turn_off_action, 127 (d, w) -> mDataController.setMobileDataEnabled(false)) 128 .create(); 129 dialog.getWindow().setType(LayoutParams.TYPE_KEYGUARD_DIALOG); 130 SystemUIDialog.setShowForAllUsers(dialog, true); 131 SystemUIDialog.registerDismissListener(dialog); 132 SystemUIDialog.setWindowOnTop(dialog); 133 dialog.show(); 134 } 135 136 @Override 137 protected void handleSecondaryClick() { 138 if (mDataController.isMobileDataSupported()) { 139 showDetail(true); 140 } else { 141 mActivityStarter 142 .postStartActivityDismissingKeyguard(getCellularSettingIntent(),0 /* delay */); 143 } 144 } 145 146 @Override 147 public CharSequence getTileLabel() { 148 return mContext.getString(R.string.quick_settings_cellular_detail_title); 149 } 150 151 @Override 152 protected void handleUpdateState(SignalState state, Object arg) { 153 CallbackInfo cb = (CallbackInfo) arg; 154 if (cb == null) { 155 cb = mSignalCallback.mInfo; 156 } 157 158 final Resources r = mContext.getResources(); 159 state.activityIn = cb.enabled && cb.activityIn; 160 state.activityOut = cb.enabled && cb.activityOut; 161 state.isOverlayIconWide = cb.isDataTypeIconWide; 162 state.overlayIconId = cb.dataTypeIconId; 163 164 state.label = r.getString(R.string.mobile_data); 165 166 final String signalContentDesc = cb.enabled && (cb.mobileSignalIconId > 0) 167 ? cb.signalContentDescription 168 : r.getString(R.string.accessibility_no_signal); 169 if (cb.noSim) { 170 state.contentDescription = state.label; 171 } else { 172 state.contentDescription = signalContentDesc + ", " + state.label; 173 } 174 175 state.expandedAccessibilityClassName = Switch.class.getName(); 176 state.value = mDataController.isMobileDataSupported() 177 && mDataController.isMobileDataEnabled(); 178 179 if (cb.noSim) { 180 state.icon = ResourceIcon.get(R.drawable.ic_qs_no_sim); 181 } else { 182 state.icon = new SignalIcon(cb.mobileSignalIconId); 183 } 184 185 if (cb.airplaneModeEnabled | cb.noSim) { 186 state.state = Tile.STATE_UNAVAILABLE; 187 } else { 188 state.state = Tile.STATE_ACTIVE; 189 } 190 } 191 192 @Override 193 public int getMetricsCategory() { 194 return MetricsEvent.QS_CELLULAR; 195 } 196 197 @Override 198 public boolean isAvailable() { 199 return mController.hasMobileDataFeature(); 200 } 201 202 // Remove the period from the network name 203 public static String removeTrailingPeriod(String string) { 204 if (string == null) return null; 205 final int length = string.length(); 206 if (string.endsWith(".")) { 207 return string.substring(0, length - 1); 208 } 209 return string; 210 } 211 212 private static final class CallbackInfo { 213 boolean enabled; 214 boolean wifiEnabled; 215 boolean airplaneModeEnabled; 216 int mobileSignalIconId; 217 String signalContentDescription; 218 int dataTypeIconId; 219 String dataContentDescription; 220 boolean activityIn; 221 boolean activityOut; 222 String enabledDesc; 223 boolean noSim; 224 boolean isDataTypeIconWide; 225 boolean roaming; 226 } 227 228 private final class CellSignalCallback implements SignalCallback { 229 private final CallbackInfo mInfo = new CallbackInfo(); 230 @Override 231 public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, 232 boolean activityIn, boolean activityOut, String description, boolean isTransient) { 233 mInfo.wifiEnabled = enabled; 234 refreshState(mInfo); 235 } 236 237 @Override 238 public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType, 239 int qsType, boolean activityIn, boolean activityOut, String typeContentDescription, 240 String description, boolean isWide, int subId, boolean roaming) { 241 if (qsIcon == null) { 242 // Not data sim, don't display. 243 return; 244 } 245 mInfo.enabled = qsIcon.visible; 246 mInfo.mobileSignalIconId = qsIcon.icon; 247 mInfo.signalContentDescription = qsIcon.contentDescription; 248 mInfo.dataTypeIconId = qsType; 249 mInfo.dataContentDescription = typeContentDescription; 250 mInfo.activityIn = activityIn; 251 mInfo.activityOut = activityOut; 252 mInfo.enabledDesc = description; 253 mInfo.isDataTypeIconWide = qsType != 0 && isWide; 254 mInfo.roaming = roaming; 255 refreshState(mInfo); 256 } 257 258 @Override 259 public void setNoSims(boolean show, boolean simDetected) { 260 mInfo.noSim = show; 261 if (mInfo.noSim) { 262 // Make sure signal gets cleared out when no sims. 263 mInfo.mobileSignalIconId = 0; 264 mInfo.dataTypeIconId = 0; 265 // Show a No SIMs description to avoid emergency calls message. 266 mInfo.enabled = true; 267 mInfo.enabledDesc = mContext.getString( 268 R.string.keyguard_missing_sim_message_short); 269 mInfo.signalContentDescription = mInfo.enabledDesc; 270 } 271 refreshState(mInfo); 272 } 273 274 @Override 275 public void setIsAirplaneMode(IconState icon) { 276 mInfo.airplaneModeEnabled = icon.visible; 277 refreshState(mInfo); 278 } 279 280 @Override 281 public void setMobileDataEnabled(boolean enabled) { 282 mDetailAdapter.setMobileDataEnabled(enabled); 283 } 284 } 285 286 static Intent getCellularSettingIntent() { 287 return new Intent(Settings.ACTION_DATA_USAGE_SETTINGS); 288 } 289 290 private final class CellularDetailAdapter implements DetailAdapter { 291 292 @Override 293 public CharSequence getTitle() { 294 return mContext.getString(R.string.quick_settings_cellular_detail_title); 295 } 296 297 @Override 298 public Boolean getToggleState() { 299 return mDataController.isMobileDataSupported() 300 ? mDataController.isMobileDataEnabled() 301 : null; 302 } 303 304 @Override 305 public Intent getSettingsIntent() { 306 return getCellularSettingIntent(); 307 } 308 309 @Override 310 public void setToggleState(boolean state) { 311 MetricsLogger.action(mContext, MetricsEvent.QS_CELLULAR_TOGGLE, state); 312 mDataController.setMobileDataEnabled(state); 313 } 314 315 @Override 316 public int getMetricsCategory() { 317 return MetricsEvent.QS_DATAUSAGEDETAIL; 318 } 319 320 @Override 321 public View createDetailView(Context context, View convertView, ViewGroup parent) { 322 final DataUsageDetailView v = (DataUsageDetailView) (convertView != null 323 ? convertView 324 : LayoutInflater.from(mContext).inflate(R.layout.data_usage, parent, false)); 325 final DataUsageController.DataUsageInfo info = mDataController.getDataUsageInfo(); 326 if (info == null) return v; 327 v.bind(info); 328 v.findViewById(R.id.roaming_text).setVisibility(mSignalCallback.mInfo.roaming 329 ? View.VISIBLE : View.INVISIBLE); 330 return v; 331 } 332 333 public void setMobileDataEnabled(boolean enabled) { 334 fireToggleStateChanged(enabled); 335 } 336 } 337} 338