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