QuickSettingsModel.java revision c86b23b9a6bc4763ff3fbe8d0ae8a9b2e21a1649
1/* 2 * Copyright (C) 2012 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.statusbar.phone; 18 19import android.bluetooth.BluetoothAdapter; 20import android.bluetooth.BluetoothAdapter.BluetoothStateChangeCallback; 21import android.content.BroadcastReceiver; 22import android.content.ContentResolver; 23import android.content.Context; 24import android.content.Intent; 25import android.content.IntentFilter; 26import android.content.pm.PackageManager; 27import android.content.res.Resources; 28import android.database.ContentObserver; 29import android.graphics.drawable.Drawable; 30import android.hardware.display.WifiDisplayStatus; 31import android.os.Handler; 32import android.provider.Settings; 33import android.provider.Settings.SettingNotFoundException; 34import android.text.TextUtils; 35import android.view.View; 36import android.view.inputmethod.InputMethodInfo; 37import android.view.inputmethod.InputMethodManager; 38import android.view.inputmethod.InputMethodSubtype; 39 40import com.android.internal.view.RotationPolicy; 41import com.android.systemui.R; 42import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; 43import com.android.systemui.statusbar.policy.BrightnessController.BrightnessStateChangeCallback; 44import com.android.systemui.statusbar.policy.LocationController.LocationGpsStateChangeCallback; 45import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback; 46 47import java.util.List; 48 49 50class QuickSettingsModel implements BluetoothStateChangeCallback, 51 NetworkSignalChangedCallback, 52 BatteryStateChangeCallback, 53 LocationGpsStateChangeCallback, 54 BrightnessStateChangeCallback { 55 56 // Sett InputMethoManagerService 57 private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher"; 58 59 /** Represents the state of a given attribute. */ 60 static class State { 61 int iconId; 62 String label; 63 boolean enabled = false; 64 } 65 static class BatteryState extends State { 66 int batteryLevel; 67 boolean pluggedIn; 68 } 69 static class RSSIState extends State { 70 int signalIconId; 71 int dataTypeIconId; 72 } 73 static class UserState extends State { 74 Drawable avatar; 75 } 76 static class BrightnessState extends State { 77 boolean autoBrightness; 78 } 79 80 /** The callback to update a given tile. */ 81 interface RefreshCallback { 82 public void refreshView(QuickSettingsTileView view, State state); 83 } 84 85 /** Broadcast receive to determine if there is an alarm set. */ 86 private BroadcastReceiver mAlarmIntentReceiver = new BroadcastReceiver() { 87 @Override 88 public void onReceive(Context context, Intent intent) { 89 String action = intent.getAction(); 90 if (action.equals(Intent.ACTION_ALARM_CHANGED)) { 91 onAlarmChanged(intent); 92 onNextAlarmChanged(); 93 } 94 } 95 }; 96 97 /** ContentObserver to determine the next alarm */ 98 private class NextAlarmObserver extends ContentObserver { 99 public NextAlarmObserver(Handler handler) { 100 super(handler); 101 } 102 103 @Override public void onChange(boolean selfChange) { 104 onNextAlarmChanged(); 105 } 106 107 public void startObserving() { 108 final ContentResolver cr = mContext.getContentResolver(); 109 cr.registerContentObserver( 110 Settings.System.getUriFor(Settings.System.NEXT_ALARM_FORMATTED), false, this); 111 } 112 } 113 114 /** ContentObserver to watch adb */ 115 private class BugreportObserver extends ContentObserver { 116 public BugreportObserver(Handler handler) { 117 super(handler); 118 } 119 120 @Override public void onChange(boolean selfChange) { 121 onBugreportChanged(); 122 } 123 124 public void startObserving() { 125 final ContentResolver cr = mContext.getContentResolver(); 126 cr.registerContentObserver( 127 Settings.Secure.getUriFor(Settings.Secure.BUGREPORT_IN_POWER_MENU), false, this); 128 } 129 } 130 private Context mContext; 131 private Handler mHandler; 132 private NextAlarmObserver mNextAlarmObserver; 133 private BugreportObserver mBugreportObserver; 134 135 private QuickSettingsTileView mUserTile; 136 private RefreshCallback mUserCallback; 137 private UserState mUserState = new UserState(); 138 139 private QuickSettingsTileView mTimeTile; 140 private RefreshCallback mTimeCallback; 141 private State mTimeState = new State(); 142 143 private QuickSettingsTileView mAlarmTile; 144 private RefreshCallback mAlarmCallback; 145 private State mAlarmState = new State(); 146 147 private QuickSettingsTileView mAirplaneModeTile; 148 private RefreshCallback mAirplaneModeCallback; 149 private State mAirplaneModeState = new State(); 150 151 private QuickSettingsTileView mWifiTile; 152 private RefreshCallback mWifiCallback; 153 private State mWifiState = new State(); 154 155 private QuickSettingsTileView mWifiDisplayTile; 156 private RefreshCallback mWifiDisplayCallback; 157 private State mWifiDisplayState = new State(); 158 159 private QuickSettingsTileView mRSSITile; 160 private RefreshCallback mRSSICallback; 161 private RSSIState mRSSIState = new RSSIState(); 162 163 private QuickSettingsTileView mBluetoothTile; 164 private RefreshCallback mBluetoothCallback; 165 private State mBluetoothState = new State(); 166 167 private QuickSettingsTileView mBatteryTile; 168 private RefreshCallback mBatteryCallback; 169 private BatteryState mBatteryState = new BatteryState(); 170 171 private QuickSettingsTileView mLocationTile; 172 private RefreshCallback mLocationCallback; 173 private State mLocationState = new State(); 174 175 private QuickSettingsTileView mImeTile; 176 private RefreshCallback mImeCallback; 177 private State mImeState = new State(); 178 179 private QuickSettingsTileView mRotationLockTile; 180 private RefreshCallback mRotationLockCallback; 181 private State mRotationLockState = new State(); 182 183 private QuickSettingsTileView mBrightnessTile; 184 private RefreshCallback mBrightnessCallback; 185 private BrightnessState mBrightnessState = new BrightnessState(); 186 187 private QuickSettingsTileView mBugreportTile; 188 private RefreshCallback mBugreportCallback; 189 private State mBugreportState = new State(); 190 191 public QuickSettingsModel(Context context) { 192 mContext = context; 193 mHandler = new Handler(); 194 mNextAlarmObserver = new NextAlarmObserver(mHandler); 195 mNextAlarmObserver.startObserving(); 196 mBugreportObserver = new BugreportObserver(mHandler); 197 mBugreportObserver.startObserving(); 198 199 IntentFilter alarmIntentFilter = new IntentFilter(); 200 alarmIntentFilter.addAction(Intent.ACTION_ALARM_CHANGED); 201 context.registerReceiver(mAlarmIntentReceiver, alarmIntentFilter); 202 } 203 204 // User 205 void addUserTile(QuickSettingsTileView view, RefreshCallback cb) { 206 mUserTile = view; 207 mUserCallback = cb; 208 mUserCallback.refreshView(mUserTile, mUserState); 209 } 210 void setUserTileInfo(String name, Drawable avatar) { 211 mUserState.label = name; 212 mUserState.avatar = avatar; 213 mUserCallback.refreshView(mUserTile, mUserState); 214 } 215 216 // Time 217 void addTimeTile(QuickSettingsTileView view, RefreshCallback cb) { 218 mTimeTile = view; 219 mTimeCallback = cb; 220 mTimeCallback.refreshView(view, mTimeState); 221 } 222 223 // Alarm 224 void addAlarmTile(QuickSettingsTileView view, RefreshCallback cb) { 225 mAlarmTile = view; 226 mAlarmCallback = cb; 227 mAlarmCallback.refreshView(view, mAlarmState); 228 } 229 void onAlarmChanged(Intent intent) { 230 mAlarmState.enabled = intent.getBooleanExtra("alarmSet", false); 231 mAlarmCallback.refreshView(mAlarmTile, mAlarmState); 232 } 233 void onNextAlarmChanged() { 234 mAlarmState.label = Settings.System.getString(mContext.getContentResolver(), 235 Settings.System.NEXT_ALARM_FORMATTED); 236 mAlarmCallback.refreshView(mAlarmTile, mAlarmState); 237 } 238 239 // Airplane Mode 240 void addAirplaneModeTile(QuickSettingsTileView view, RefreshCallback cb) { 241 mAirplaneModeTile = view; 242 mAirplaneModeTile.setOnClickListener(new View.OnClickListener() { 243 @Override 244 public void onClick(View v) { 245 if (mAirplaneModeState.enabled) { 246 setAirplaneModeState(false); 247 } else { 248 setAirplaneModeState(true); 249 } 250 } 251 }); 252 mAirplaneModeCallback = cb; 253 mAirplaneModeCallback.refreshView(mAirplaneModeTile, mAirplaneModeState); 254 } 255 private void setAirplaneModeState(boolean enabled) { 256 // TODO: Sets the view to be "awaiting" if not already awaiting 257 258 // Change the system setting 259 Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 260 enabled ? 1 : 0); 261 262 // Post the intent 263 Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); 264 intent.putExtra("state", enabled); 265 mContext.sendBroadcast(intent); 266 } 267 // NetworkSignalChanged callback 268 @Override 269 public void onAirplaneModeChanged(boolean enabled) { 270 // TODO: If view is in awaiting state, disable 271 Resources r = mContext.getResources(); 272 mAirplaneModeState.enabled = enabled; 273 mAirplaneModeState.iconId = (enabled ? 274 R.drawable.ic_qs_airplane_on : 275 R.drawable.ic_qs_airplane_off); 276 mAirplaneModeCallback.refreshView(mAirplaneModeTile, mAirplaneModeState); 277 } 278 279 // Wifi 280 void addWifiTile(QuickSettingsTileView view, RefreshCallback cb) { 281 mWifiTile = view; 282 mWifiCallback = cb; 283 mWifiCallback.refreshView(mWifiTile, mWifiState); 284 } 285 // Remove the double quotes that the SSID may contain 286 public static String removeDoubleQuotes(String string) { 287 if (string == null) return null; 288 final int length = string.length(); 289 if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) { 290 return string.substring(1, length - 1); 291 } 292 return string; 293 } 294 // Remove the period from the network name 295 public static String removeTrailingPeriod(String string) { 296 if (string == null) return null; 297 final int length = string.length(); 298 if (string.endsWith(".")) { 299 string.substring(0, length - 1); 300 } 301 return string; 302 } 303 // NetworkSignalChanged callback 304 @Override 305 public void onWifiSignalChanged(boolean enabled, int wifiSignalIconId, String enabledDesc) { 306 // TODO: If view is in awaiting state, disable 307 Resources r = mContext.getResources(); 308 mWifiState.enabled = enabled; 309 boolean wifiConnected = enabled && (wifiSignalIconId > 0) && (enabledDesc != null); 310 boolean wifiNotConnected = enabled && (enabledDesc == null); 311 if (wifiConnected) { 312 mWifiState.iconId = wifiSignalIconId; 313 mWifiState.label = removeDoubleQuotes(enabledDesc); 314 } else if (wifiNotConnected) { 315 mWifiState.iconId = R.drawable.ic_qs_wifi_0; 316 mWifiState.label = r.getString(R.string.quick_settings_wifi_not_connected); 317 } else { 318 mWifiState.iconId = R.drawable.ic_qs_wifi_no_network; 319 mWifiState.label = r.getString(R.string.quick_settings_wifi_off_label); 320 } 321 mWifiCallback.refreshView(mWifiTile, mWifiState); 322 } 323 324 // RSSI 325 void addRSSITile(QuickSettingsTileView view, RefreshCallback cb) { 326 mRSSITile = view; 327 mRSSICallback = cb; 328 mRSSICallback.refreshView(mRSSITile, mRSSIState); 329 } 330 boolean deviceSupportsTelephony() { 331 PackageManager pm = mContext.getPackageManager(); 332 return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY); 333 } 334 // NetworkSignalChanged callback 335 @Override 336 public void onMobileDataSignalChanged(boolean enabled, int mobileSignalIconId, 337 int dataTypeIconId, String enabledDesc) { 338 if (deviceSupportsTelephony()) { 339 // TODO: If view is in awaiting state, disable 340 Resources r = mContext.getResources(); 341 mRSSIState.signalIconId = enabled && (mobileSignalIconId > 0) 342 ? mobileSignalIconId 343 : R.drawable.ic_qs_signal_no_signal; 344 mRSSIState.dataTypeIconId = enabled && (dataTypeIconId > 0) && !mWifiState.enabled 345 ? dataTypeIconId 346 : 0; 347 mRSSIState.label = enabled 348 ? removeTrailingPeriod(enabledDesc) 349 : r.getString(R.string.quick_settings_rssi_emergency_only); 350 mRSSICallback.refreshView(mRSSITile, mRSSIState); 351 } 352 } 353 354 // Bluetooth 355 void addBluetoothTile(QuickSettingsTileView view, RefreshCallback cb) { 356 mBluetoothTile = view; 357 mBluetoothCallback = cb; 358 359 final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 360 onBluetoothStateChange(adapter.isEnabled()); 361 } 362 boolean deviceSupportsBluetooth() { 363 return (BluetoothAdapter.getDefaultAdapter() != null); 364 } 365 // BluetoothController callback 366 @Override 367 public void onBluetoothStateChange(boolean on) { 368 // TODO: If view is in awaiting state, disable 369 Resources r = mContext.getResources(); 370 mBluetoothState.enabled = on; 371 if (on) { 372 mBluetoothState.iconId = R.drawable.ic_qs_bluetooth_on; 373 mBluetoothState.label = r.getString(R.string.quick_settings_bluetooth_label); 374 } else { 375 mBluetoothState.iconId = R.drawable.ic_qs_bluetooth_off; 376 mBluetoothState.label = r.getString(R.string.quick_settings_bluetooth_off_label); 377 } 378 mBluetoothCallback.refreshView(mBluetoothTile, mBluetoothState); 379 } 380 381 // Battery 382 void addBatteryTile(QuickSettingsTileView view, RefreshCallback cb) { 383 mBatteryTile = view; 384 mBatteryCallback = cb; 385 mBatteryCallback.refreshView(mBatteryTile, mBatteryState); 386 } 387 // BatteryController callback 388 @Override 389 public void onBatteryLevelChanged(int level, boolean pluggedIn) { 390 mBatteryState.batteryLevel = level; 391 mBatteryState.pluggedIn = pluggedIn; 392 mBatteryCallback.refreshView(mBatteryTile, mBatteryState); 393 } 394 395 // Location 396 void addLocationTile(QuickSettingsTileView view, RefreshCallback cb) { 397 mLocationTile = view; 398 mLocationCallback = cb; 399 mLocationCallback.refreshView(mLocationTile, mLocationState); 400 } 401 // LocationController callback 402 @Override 403 public void onLocationGpsStateChanged(boolean inUse, String description) { 404 mLocationState.enabled = inUse; 405 mLocationState.label = description; 406 mLocationCallback.refreshView(mLocationTile, mLocationState); 407 } 408 409 // Bug report 410 void addBugreportTile(QuickSettingsTileView view, RefreshCallback cb) { 411 mBugreportTile = view; 412 mBugreportCallback = cb; 413 onBugreportChanged(); 414 } 415 // SettingsObserver callback 416 public void onBugreportChanged() { 417 final ContentResolver cr = mContext.getContentResolver(); 418 boolean enabled = false; 419 try { 420 enabled = (Settings.Secure.getInt(cr, Settings.Secure.BUGREPORT_IN_POWER_MENU) != 0); 421 } catch (SettingNotFoundException e) { 422 } 423 424 mBugreportState.enabled = enabled; 425 mBugreportCallback.refreshView(mBugreportTile, mBugreportState); 426 } 427 428 // Wifi Display 429 void addWifiDisplayTile(QuickSettingsTileView view, RefreshCallback cb) { 430 mWifiDisplayTile = view; 431 mWifiDisplayCallback = cb; 432 } 433 public void onWifiDisplayStateChanged(WifiDisplayStatus status) { 434 mWifiDisplayState.enabled = 435 (status.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON); 436 if (status.getActiveDisplay() != null) { 437 mWifiDisplayState.label = status.getActiveDisplay().getFriendlyDisplayName(); 438 } else { 439 mWifiDisplayState.label = mContext.getString( 440 R.string.quick_settings_wifi_display_no_connection_label); 441 } 442 mWifiDisplayCallback.refreshView(mWifiDisplayTile, mWifiDisplayState); 443 444 } 445 446 // IME 447 void addImeTile(QuickSettingsTileView view, RefreshCallback cb) { 448 mImeTile = view; 449 mImeCallback = cb; 450 mImeCallback.refreshView(mImeTile, mImeState); 451 } 452 /* This implementation is taken from 453 InputMethodManagerService.needsToShowImeSwitchOngoingNotification(). */ 454 private boolean needsToShowImeSwitchOngoingNotification(InputMethodManager imm) { 455 List<InputMethodInfo> imis = imm.getEnabledInputMethodList(); 456 final int N = imis.size(); 457 if (N > 2) return true; 458 if (N < 1) return false; 459 int nonAuxCount = 0; 460 int auxCount = 0; 461 InputMethodSubtype nonAuxSubtype = null; 462 InputMethodSubtype auxSubtype = null; 463 for(int i = 0; i < N; ++i) { 464 final InputMethodInfo imi = imis.get(i); 465 final List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(imi, 466 true); 467 final int subtypeCount = subtypes.size(); 468 if (subtypeCount == 0) { 469 ++nonAuxCount; 470 } else { 471 for (int j = 0; j < subtypeCount; ++j) { 472 final InputMethodSubtype subtype = subtypes.get(j); 473 if (!subtype.isAuxiliary()) { 474 ++nonAuxCount; 475 nonAuxSubtype = subtype; 476 } else { 477 ++auxCount; 478 auxSubtype = subtype; 479 } 480 } 481 } 482 } 483 if (nonAuxCount > 1 || auxCount > 1) { 484 return true; 485 } else if (nonAuxCount == 1 && auxCount == 1) { 486 if (nonAuxSubtype != null && auxSubtype != null 487 && (nonAuxSubtype.getLocale().equals(auxSubtype.getLocale()) 488 || auxSubtype.overridesImplicitlyEnabledSubtype() 489 || nonAuxSubtype.overridesImplicitlyEnabledSubtype()) 490 && nonAuxSubtype.containsExtraValueKey(TAG_TRY_SUPPRESSING_IME_SWITCHER)) { 491 return false; 492 } 493 return true; 494 } 495 return false; 496 } 497 void onImeWindowStatusChanged(boolean visible) { 498 InputMethodManager imm = 499 (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); 500 List<InputMethodInfo> imis = imm.getInputMethodList(); 501 502 mImeState.enabled = (visible && needsToShowImeSwitchOngoingNotification(imm)); 503 mImeState.label = getCurrentInputMethodName(mContext, mContext.getContentResolver(), 504 imm, imis, mContext.getPackageManager()); 505 mImeCallback.refreshView(mImeTile, mImeState); 506 } 507 private static String getCurrentInputMethodName(Context context, ContentResolver resolver, 508 InputMethodManager imm, List<InputMethodInfo> imis, PackageManager pm) { 509 if (resolver == null || imis == null) return null; 510 final String currentInputMethodId = Settings.Secure.getString(resolver, 511 Settings.Secure.DEFAULT_INPUT_METHOD); 512 if (TextUtils.isEmpty(currentInputMethodId)) return null; 513 for (InputMethodInfo imi : imis) { 514 if (currentInputMethodId.equals(imi.getId())) { 515 final InputMethodSubtype subtype = imm.getCurrentInputMethodSubtype(); 516 final CharSequence summary = subtype != null 517 ? subtype.getDisplayName(context, imi.getPackageName(), 518 imi.getServiceInfo().applicationInfo) 519 : context.getString(R.string.quick_settings_ime_label); 520 return summary.toString(); 521 } 522 } 523 return null; 524 } 525 526 // Rotation lock 527 void addRotationLockTile(QuickSettingsTileView view, RefreshCallback cb) { 528 mRotationLockTile = view; 529 mRotationLockCallback = cb; 530 onRotationLockChanged(); 531 } 532 void onRotationLockChanged() { 533 boolean locked = RotationPolicy.isRotationLocked(mContext); 534 mRotationLockState.enabled = locked; 535 mRotationLockState.iconId = locked 536 ? R.drawable.ic_qs_rotation_locked 537 : R.drawable.ic_qs_auto_rotate; 538 mRotationLockState.label = locked 539 ? mContext.getString(R.string.quick_settings_rotation_locked_label) 540 : mContext.getString(R.string.quick_settings_rotation_unlocked_label); 541 542 // may be called before addRotationLockTile due to RotationPolicyListener in QuickSettings 543 if (mRotationLockTile != null && mRotationLockCallback != null) { 544 mRotationLockCallback.refreshView(mRotationLockTile, mRotationLockState); 545 } 546 } 547 548 // Brightness 549 void addBrightnessTile(QuickSettingsTileView view, RefreshCallback cb) { 550 mBrightnessTile = view; 551 mBrightnessCallback = cb; 552 onBrightnessLevelChanged(); 553 } 554 @Override 555 public void onBrightnessLevelChanged() { 556 int mode = Settings.System.getInt(mContext.getContentResolver(), 557 Settings.System.SCREEN_BRIGHTNESS_MODE, 558 Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); 559 mBrightnessState.autoBrightness = 560 (mode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); 561 mBrightnessState.iconId = mBrightnessState.autoBrightness 562 ? R.drawable.ic_qs_brightness_auto_on 563 : R.drawable.ic_qs_brightness_auto_off; 564 mBrightnessCallback.refreshView(mBrightnessTile, mBrightnessState); 565 } 566 567}