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 17 package com.android.systemui.statusbar.phone; 18 19 import android.animation.ValueAnimator; 20 import android.app.ActivityManagerNative; 21 import android.app.AlertDialog; 22 import android.app.Dialog; 23 import android.app.PendingIntent; 24 import android.app.admin.DevicePolicyManager; 25 import android.bluetooth.BluetoothAdapter; 26 import android.content.BroadcastReceiver; 27 import android.content.ComponentName; 28 import android.content.Context; 29 import android.content.DialogInterface; 30 import android.content.DialogInterface.OnClickListener; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.content.pm.PackageManager.NameNotFoundException; 34 import android.content.pm.UserInfo; 35 import android.content.res.Resources; 36 import android.database.Cursor; 37 import android.graphics.Bitmap; 38 import android.graphics.drawable.BitmapDrawable; 39 import android.graphics.drawable.Drawable; 40 import android.hardware.display.DisplayManager; 41 import android.media.MediaRouter; 42 import android.net.wifi.WifiManager; 43 import android.os.AsyncTask; 44 import android.os.Handler; 45 import android.os.RemoteException; 46 import android.os.UserHandle; 47 import android.os.UserManager; 48 import android.provider.AlarmClock; 49 import android.provider.ContactsContract; 50 import android.provider.ContactsContract.CommonDataKinds.Phone; 51 import android.provider.ContactsContract.Profile; 52 import android.provider.Settings; 53 import android.security.KeyChain; 54 import android.util.Log; 55 import android.util.Pair; 56 import android.view.LayoutInflater; 57 import android.view.View; 58 import android.view.ViewGroup; 59 import android.view.WindowManager; 60 import android.view.WindowManagerGlobal; 61 import android.widget.ImageView; 62 import android.widget.TextView; 63 64 import com.android.internal.app.MediaRouteDialogPresenter; 65 import com.android.systemui.R; 66 import com.android.systemui.statusbar.phone.QuickSettingsModel.ActivityState; 67 import com.android.systemui.statusbar.phone.QuickSettingsModel.BluetoothState; 68 import com.android.systemui.statusbar.phone.QuickSettingsModel.RSSIState; 69 import com.android.systemui.statusbar.phone.QuickSettingsModel.State; 70 import com.android.systemui.statusbar.phone.QuickSettingsModel.UserState; 71 import com.android.systemui.statusbar.phone.QuickSettingsModel.WifiState; 72 import com.android.systemui.statusbar.policy.BatteryController; 73 import com.android.systemui.statusbar.policy.BluetoothController; 74 import com.android.systemui.statusbar.policy.LocationController; 75 import com.android.systemui.statusbar.policy.NetworkController; 76 import com.android.systemui.statusbar.policy.RotationLockController; 77 78 import java.util.ArrayList; 79 80 /** 81 * 82 */ 83 class QuickSettings { 84 static final boolean DEBUG_GONE_TILES = false; 85 private static final String TAG = "QuickSettings"; 86 public static final boolean SHOW_IME_TILE = false; 87 88 public static final boolean LONG_PRESS_TOGGLES = true; 89 90 private Context mContext; 91 private PanelBar mBar; 92 private QuickSettingsModel mModel; 93 private ViewGroup mContainerView; 94 95 private DevicePolicyManager mDevicePolicyManager; 96 private PhoneStatusBar mStatusBarService; 97 private BluetoothState mBluetoothState; 98 private BluetoothAdapter mBluetoothAdapter; 99 private WifiManager mWifiManager; 100 101 private BluetoothController mBluetoothController; 102 private RotationLockController mRotationLockController; 103 private LocationController mLocationController; 104 105 private AsyncTask<Void, Void, Pair<String, Drawable>> mUserInfoTask; 106 private AsyncTask<Void, Void, Pair<Boolean, Boolean>> mQueryCertTask; 107 108 boolean mTilesSetUp = false; 109 boolean mUseDefaultAvatar = false; 110 111 private Handler mHandler; 112 113 // The set of QuickSettingsTiles that have dynamic spans (and need to be updated on 114 // configuration change) 115 private final ArrayList<QuickSettingsTileView> mDynamicSpannedTiles = 116 new ArrayList<QuickSettingsTileView>(); 117 118 public QuickSettings(Context context, QuickSettingsContainerView container) { 119 mDevicePolicyManager 120 = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); 121 mContext = context; 122 mContainerView = container; 123 mModel = new QuickSettingsModel(context); 124 mBluetoothState = new QuickSettingsModel.BluetoothState(); 125 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 126 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 127 128 mHandler = new Handler(); 129 130 IntentFilter filter = new IntentFilter(); 131 filter.addAction(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED); 132 filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); 133 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); 134 filter.addAction(Intent.ACTION_USER_SWITCHED); 135 filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); 136 filter.addAction(KeyChain.ACTION_STORAGE_CHANGED); 137 mContext.registerReceiver(mReceiver, filter); 138 139 IntentFilter profileFilter = new IntentFilter(); 140 profileFilter.addAction(ContactsContract.Intents.ACTION_PROFILE_CHANGED); 141 profileFilter.addAction(Intent.ACTION_USER_INFO_CHANGED); 142 mContext.registerReceiverAsUser(mProfileReceiver, UserHandle.ALL, profileFilter, 143 null, null); 144 } 145 146 void setBar(PanelBar bar) { 147 mBar = bar; 148 } 149 150 public void setService(PhoneStatusBar phoneStatusBar) { 151 mStatusBarService = phoneStatusBar; 152 } 153 154 public PhoneStatusBar getService() { 155 return mStatusBarService; 156 } 157 158 public void setImeWindowStatus(boolean visible) { 159 mModel.onImeWindowStatusChanged(visible); 160 } 161 162 void setup(NetworkController networkController, BluetoothController bluetoothController, 163 BatteryController batteryController, LocationController locationController, 164 RotationLockController rotationLockController) { 165 mBluetoothController = bluetoothController; 166 mRotationLockController = rotationLockController; 167 mLocationController = locationController; 168 169 setupQuickSettings(); 170 updateResources(); 171 applyLocationEnabledStatus(); 172 173 networkController.addNetworkSignalChangedCallback(mModel); 174 bluetoothController.addStateChangedCallback(mModel); 175 batteryController.addStateChangedCallback(mModel); 176 locationController.addSettingsChangedCallback(mModel); 177 rotationLockController.addRotationLockControllerCallback(mModel); 178 } 179 180 private void queryForSslCaCerts() { 181 mQueryCertTask = new AsyncTask<Void, Void, Pair<Boolean, Boolean>>() { 182 @Override 183 protected Pair<Boolean, Boolean> doInBackground(Void... params) { 184 boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled(); 185 boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null; 186 187 return Pair.create(hasCert, isManaged); 188 } 189 @Override 190 protected void onPostExecute(Pair<Boolean, Boolean> result) { 191 super.onPostExecute(result); 192 boolean hasCert = result.first; 193 boolean isManaged = result.second; 194 mModel.setSslCaCertWarningTileInfo(hasCert, isManaged); 195 } 196 }; 197 mQueryCertTask.execute(); 198 } 199 200 private void queryForUserInformation() { 201 Context currentUserContext = null; 202 UserInfo userInfo = null; 203 try { 204 userInfo = ActivityManagerNative.getDefault().getCurrentUser(); 205 currentUserContext = mContext.createPackageContextAsUser("android", 0, 206 new UserHandle(userInfo.id)); 207 } catch (NameNotFoundException e) { 208 Log.e(TAG, "Couldn't create user context", e); 209 throw new RuntimeException(e); 210 } catch (RemoteException e) { 211 Log.e(TAG, "Couldn't get user info", e); 212 } 213 final int userId = userInfo.id; 214 final String userName = userInfo.name; 215 216 final Context context = currentUserContext; 217 mUserInfoTask = new AsyncTask<Void, Void, Pair<String, Drawable>>() { 218 @Override 219 protected Pair<String, Drawable> doInBackground(Void... params) { 220 final UserManager um = UserManager.get(mContext); 221 222 // Fall back to the UserManager nickname if we can't read the name from the local 223 // profile below. 224 String name = userName; 225 Drawable avatar = null; 226 Bitmap rawAvatar = um.getUserIcon(userId); 227 if (rawAvatar != null) { 228 avatar = new BitmapDrawable(mContext.getResources(), rawAvatar); 229 } else { 230 avatar = mContext.getResources().getDrawable(R.drawable.ic_qs_default_user); 231 mUseDefaultAvatar = true; 232 } 233 234 // If it's a single-user device, get the profile name, since the nickname is not 235 // usually valid 236 if (um.getUsers().size() <= 1) { 237 // Try and read the display name from the local profile 238 final Cursor cursor = context.getContentResolver().query( 239 Profile.CONTENT_URI, new String[] {Phone._ID, Phone.DISPLAY_NAME}, 240 null, null, null); 241 if (cursor != null) { 242 try { 243 if (cursor.moveToFirst()) { 244 name = cursor.getString(cursor.getColumnIndex(Phone.DISPLAY_NAME)); 245 } 246 } finally { 247 cursor.close(); 248 } 249 } 250 } 251 return new Pair<String, Drawable>(name, avatar); 252 } 253 254 @Override 255 protected void onPostExecute(Pair<String, Drawable> result) { 256 super.onPostExecute(result); 257 mModel.setUserTileInfo(result.first, result.second); 258 mUserInfoTask = null; 259 } 260 }; 261 mUserInfoTask.execute(); 262 } 263 264 private void setupQuickSettings() { 265 // Setup the tiles that we are going to be showing (including the temporary ones) 266 LayoutInflater inflater = LayoutInflater.from(mContext); 267 268 addUserTiles(mContainerView, inflater); 269 addSystemTiles(mContainerView, inflater); 270 addTemporaryTiles(mContainerView, inflater); 271 272 queryForUserInformation(); 273 queryForSslCaCerts(); 274 mTilesSetUp = true; 275 } 276 277 private void startSettingsActivity(String action) { 278 Intent intent = new Intent(action); 279 startSettingsActivity(intent); 280 } 281 282 private void startSettingsActivity(Intent intent) { 283 startSettingsActivity(intent, true); 284 } 285 286 private void collapsePanels() { 287 getService().animateCollapsePanels(); 288 } 289 290 private void startSettingsActivity(Intent intent, boolean onlyProvisioned) { 291 if (onlyProvisioned && !getService().isDeviceProvisioned()) return; 292 try { 293 // Dismiss the lock screen when Settings starts. 294 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); 295 } catch (RemoteException e) { 296 } 297 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); 298 mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); 299 collapsePanels(); 300 } 301 302 private void addUserTiles(ViewGroup parent, LayoutInflater inflater) { 303 QuickSettingsTileView userTile = (QuickSettingsTileView) 304 inflater.inflate(R.layout.quick_settings_tile, parent, false); 305 userTile.setContent(R.layout.quick_settings_tile_user, inflater); 306 userTile.setOnClickListener(new View.OnClickListener() { 307 @Override 308 public void onClick(View v) { 309 collapsePanels(); 310 final UserManager um = UserManager.get(mContext); 311 if (um.getUsers(true).size() > 1) { 312 // Since keyguard and systemui were merged into the same process to save 313 // memory, they share the same Looper and graphics context. As a result, 314 // there's no way to allow concurrent animation while keyguard inflates. 315 // The workaround is to add a slight delay to allow the animation to finish. 316 mHandler.postDelayed(new Runnable() { 317 public void run() { 318 try { 319 WindowManagerGlobal.getWindowManagerService().lockNow(null); 320 } catch (RemoteException e) { 321 Log.e(TAG, "Couldn't show user switcher", e); 322 } 323 } 324 }, 400); // TODO: ideally this would be tied to the collapse of the panel 325 } else { 326 Intent intent = ContactsContract.QuickContact.composeQuickContactsIntent( 327 mContext, v, ContactsContract.Profile.CONTENT_URI, 328 ContactsContract.QuickContact.MODE_LARGE, null); 329 mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); 330 } 331 } 332 }); 333 mModel.addUserTile(userTile, new QuickSettingsModel.RefreshCallback() { 334 @Override 335 public void refreshView(QuickSettingsTileView view, State state) { 336 UserState us = (UserState) state; 337 ImageView iv = (ImageView) view.findViewById(R.id.user_imageview); 338 TextView tv = (TextView) view.findViewById(R.id.user_textview); 339 tv.setText(state.label); 340 iv.setImageDrawable(us.avatar); 341 view.setContentDescription(mContext.getString( 342 R.string.accessibility_quick_settings_user, state.label)); 343 } 344 }); 345 parent.addView(userTile); 346 mDynamicSpannedTiles.add(userTile); 347 348 // Brightness 349 final QuickSettingsBasicTile brightnessTile 350 = new QuickSettingsBasicTile(mContext); 351 brightnessTile.setImageResource(R.drawable.ic_qs_brightness_auto_off); 352 brightnessTile.setOnClickListener(new View.OnClickListener() { 353 @Override 354 public void onClick(View v) { 355 collapsePanels(); 356 showBrightnessDialog(); 357 } 358 }); 359 mModel.addBrightnessTile(brightnessTile, 360 new QuickSettingsModel.BasicRefreshCallback(brightnessTile)); 361 parent.addView(brightnessTile); 362 mDynamicSpannedTiles.add(brightnessTile); 363 364 // Settings tile 365 final QuickSettingsBasicTile settingsTile = new QuickSettingsBasicTile(mContext); 366 settingsTile.setImageResource(R.drawable.ic_qs_settings); 367 settingsTile.setOnClickListener(new View.OnClickListener() { 368 @Override 369 public void onClick(View v) { 370 startSettingsActivity(android.provider.Settings.ACTION_SETTINGS); 371 } 372 }); 373 mModel.addSettingsTile(settingsTile, 374 new QuickSettingsModel.BasicRefreshCallback(settingsTile)); 375 parent.addView(settingsTile); 376 mDynamicSpannedTiles.add(settingsTile); 377 } 378 379 private void addSystemTiles(ViewGroup parent, LayoutInflater inflater) { 380 // Wi-fi 381 final QuickSettingsTileView wifiTile = (QuickSettingsTileView) 382 inflater.inflate(R.layout.quick_settings_tile, parent, false); 383 wifiTile.setContent(R.layout.quick_settings_tile_wifi, inflater); 384 wifiTile.setOnClickListener(new View.OnClickListener() { 385 @Override 386 public void onClick(View v) { 387 startSettingsActivity(android.provider.Settings.ACTION_WIFI_SETTINGS); 388 } 389 }); 390 if (LONG_PRESS_TOGGLES) { 391 wifiTile.setOnLongClickListener(new View.OnLongClickListener() { 392 @Override 393 public boolean onLongClick(View v) { 394 final boolean enable = 395 (mWifiManager.getWifiState() != WifiManager.WIFI_STATE_ENABLED); 396 new AsyncTask<Void, Void, Void>() { 397 @Override 398 protected Void doInBackground(Void... args) { 399 // Disable tethering if enabling Wifi 400 final int wifiApState = mWifiManager.getWifiApState(); 401 if (enable && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) || 402 (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) { 403 mWifiManager.setWifiApEnabled(null, false); 404 } 405 406 mWifiManager.setWifiEnabled(enable); 407 return null; 408 } 409 }.execute(); 410 wifiTile.setPressed(false); 411 return true; 412 }} ); 413 } 414 mModel.addWifiTile(wifiTile, new NetworkActivityCallback() { 415 @Override 416 public void refreshView(QuickSettingsTileView view, State state) { 417 WifiState wifiState = (WifiState) state; 418 ImageView iv = (ImageView) view.findViewById(R.id.image); 419 iv.setImageResource(wifiState.iconId); 420 setActivity(view, wifiState); 421 TextView tv = (TextView) view.findViewById(R.id.text); 422 tv.setText(wifiState.label); 423 wifiTile.setContentDescription(mContext.getString( 424 R.string.accessibility_quick_settings_wifi, 425 wifiState.signalContentDescription, 426 (wifiState.connected) ? wifiState.label : "")); 427 } 428 }); 429 parent.addView(wifiTile); 430 431 if (mModel.deviceHasMobileData()) { 432 // RSSI 433 QuickSettingsTileView rssiTile = (QuickSettingsTileView) 434 inflater.inflate(R.layout.quick_settings_tile, parent, false); 435 rssiTile.setContent(R.layout.quick_settings_tile_rssi, inflater); 436 rssiTile.setOnClickListener(new View.OnClickListener() { 437 @Override 438 public void onClick(View v) { 439 Intent intent = new Intent(); 440 intent.setComponent(new ComponentName( 441 "com.android.settings", 442 "com.android.settings.Settings$DataUsageSummaryActivity")); 443 startSettingsActivity(intent); 444 } 445 }); 446 mModel.addRSSITile(rssiTile, new NetworkActivityCallback() { 447 @Override 448 public void refreshView(QuickSettingsTileView view, State state) { 449 RSSIState rssiState = (RSSIState) state; 450 ImageView iv = (ImageView) view.findViewById(R.id.rssi_image); 451 ImageView iov = (ImageView) view.findViewById(R.id.rssi_overlay_image); 452 TextView tv = (TextView) view.findViewById(R.id.rssi_textview); 453 // Force refresh 454 iv.setImageDrawable(null); 455 iv.setImageResource(rssiState.signalIconId); 456 457 if (rssiState.dataTypeIconId > 0) { 458 iov.setImageResource(rssiState.dataTypeIconId); 459 } else { 460 iov.setImageDrawable(null); 461 } 462 setActivity(view, rssiState); 463 464 tv.setText(state.label); 465 view.setContentDescription(mContext.getResources().getString( 466 R.string.accessibility_quick_settings_mobile, 467 rssiState.signalContentDescription, rssiState.dataContentDescription, 468 state.label)); 469 } 470 }); 471 parent.addView(rssiTile); 472 } 473 474 // Rotation Lock 475 if (mContext.getResources().getBoolean(R.bool.quick_settings_show_rotation_lock) 476 || DEBUG_GONE_TILES) { 477 final QuickSettingsBasicTile rotationLockTile 478 = new QuickSettingsBasicTile(mContext); 479 rotationLockTile.setOnClickListener(new View.OnClickListener() { 480 @Override 481 public void onClick(View view) { 482 final boolean locked = mRotationLockController.isRotationLocked(); 483 mRotationLockController.setRotationLocked(!locked); 484 } 485 }); 486 mModel.addRotationLockTile(rotationLockTile, mRotationLockController, 487 new QuickSettingsModel.RefreshCallback() { 488 @Override 489 public void refreshView(QuickSettingsTileView view, State state) { 490 QuickSettingsModel.RotationLockState rotationLockState = 491 (QuickSettingsModel.RotationLockState) state; 492 view.setVisibility(rotationLockState.visible 493 ? View.VISIBLE : View.GONE); 494 if (state.iconId != 0) { 495 // needed to flush any cached IDs 496 rotationLockTile.setImageDrawable(null); 497 rotationLockTile.setImageResource(state.iconId); 498 } 499 if (state.label != null) { 500 rotationLockTile.setText(state.label); 501 } 502 } 503 }); 504 parent.addView(rotationLockTile); 505 } 506 507 // Battery 508 final QuickSettingsTileView batteryTile = (QuickSettingsTileView) 509 inflater.inflate(R.layout.quick_settings_tile, parent, false); 510 batteryTile.setContent(R.layout.quick_settings_tile_battery, inflater); 511 batteryTile.setOnClickListener(new View.OnClickListener() { 512 @Override 513 public void onClick(View v) { 514 startSettingsActivity(Intent.ACTION_POWER_USAGE_SUMMARY); 515 } 516 }); 517 mModel.addBatteryTile(batteryTile, new QuickSettingsModel.RefreshCallback() { 518 @Override 519 public void refreshView(QuickSettingsTileView unused, State state) { 520 QuickSettingsModel.BatteryState batteryState = 521 (QuickSettingsModel.BatteryState) state; 522 String t; 523 if (batteryState.batteryLevel == 100) { 524 t = mContext.getString(R.string.quick_settings_battery_charged_label); 525 } else { 526 t = batteryState.pluggedIn 527 ? mContext.getString(R.string.quick_settings_battery_charging_label, 528 batteryState.batteryLevel) 529 : mContext.getString(R.string.status_bar_settings_battery_meter_format, 530 batteryState.batteryLevel); 531 } 532 ((TextView)batteryTile.findViewById(R.id.text)).setText(t); 533 batteryTile.setContentDescription( 534 mContext.getString(R.string.accessibility_quick_settings_battery, t)); 535 } 536 }); 537 parent.addView(batteryTile); 538 539 // Airplane Mode 540 final QuickSettingsBasicTile airplaneTile 541 = new QuickSettingsBasicTile(mContext); 542 mModel.addAirplaneModeTile(airplaneTile, new QuickSettingsModel.RefreshCallback() { 543 @Override 544 public void refreshView(QuickSettingsTileView unused, State state) { 545 airplaneTile.setImageResource(state.iconId); 546 547 String airplaneState = mContext.getString( 548 (state.enabled) ? R.string.accessibility_desc_on 549 : R.string.accessibility_desc_off); 550 airplaneTile.setContentDescription( 551 mContext.getString(R.string.accessibility_quick_settings_airplane, airplaneState)); 552 airplaneTile.setText(state.label); 553 } 554 }); 555 parent.addView(airplaneTile); 556 557 // Bluetooth 558 if (mModel.deviceSupportsBluetooth() 559 || DEBUG_GONE_TILES) { 560 final QuickSettingsBasicTile bluetoothTile 561 = new QuickSettingsBasicTile(mContext); 562 bluetoothTile.setOnClickListener(new View.OnClickListener() { 563 @Override 564 public void onClick(View v) { 565 startSettingsActivity(android.provider.Settings.ACTION_BLUETOOTH_SETTINGS); 566 } 567 }); 568 if (LONG_PRESS_TOGGLES) { 569 bluetoothTile.setOnLongClickListener(new View.OnLongClickListener() { 570 @Override 571 public boolean onLongClick(View v) { 572 if (mBluetoothAdapter.isEnabled()) { 573 mBluetoothAdapter.disable(); 574 } else { 575 mBluetoothAdapter.enable(); 576 } 577 bluetoothTile.setPressed(false); 578 return true; 579 }}); 580 } 581 mModel.addBluetoothTile(bluetoothTile, new QuickSettingsModel.RefreshCallback() { 582 @Override 583 public void refreshView(QuickSettingsTileView unused, State state) { 584 BluetoothState bluetoothState = (BluetoothState) state; 585 bluetoothTile.setImageResource(state.iconId); 586 587 /* 588 Resources r = mContext.getResources(); 589 //TODO: Show connected bluetooth device label 590 Set<BluetoothDevice> btDevices = 591 mBluetoothController.getBondedBluetoothDevices(); 592 if (btDevices.size() == 1) { 593 // Show the name of the bluetooth device you are connected to 594 label = btDevices.iterator().next().getName(); 595 } else if (btDevices.size() > 1) { 596 // Show a generic label about the number of bluetooth devices 597 label = r.getString(R.string.quick_settings_bluetooth_multiple_devices_label, 598 btDevices.size()); 599 } 600 */ 601 bluetoothTile.setContentDescription(mContext.getString( 602 R.string.accessibility_quick_settings_bluetooth, 603 bluetoothState.stateContentDescription)); 604 bluetoothTile.setText(state.label); 605 } 606 }); 607 parent.addView(bluetoothTile); 608 } 609 610 // Location 611 final QuickSettingsBasicTile locationTile 612 = new QuickSettingsBasicTile(mContext); 613 locationTile.setImageResource(R.drawable.ic_qs_location_on); 614 locationTile.setTextResource(R.string.quick_settings_location_label); 615 locationTile.setOnClickListener(new View.OnClickListener() { 616 @Override 617 public void onClick(View v) { 618 startSettingsActivity(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS); 619 } 620 }); 621 if (LONG_PRESS_TOGGLES) { 622 locationTile.setOnLongClickListener(new View.OnLongClickListener() { 623 @Override 624 public boolean onLongClick(View v) { 625 boolean newLocationEnabledState = !mLocationController.isLocationEnabled(); 626 if (mLocationController.setLocationEnabled(newLocationEnabledState) 627 && newLocationEnabledState) { 628 // If we've successfully switched from location off to on, close the 629 // notifications tray to show the network location provider consent dialog. 630 Intent closeDialog = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 631 mContext.sendBroadcast(closeDialog); 632 } 633 return true; // Consume click 634 }} ); 635 } 636 mModel.addLocationTile(locationTile, new QuickSettingsModel.RefreshCallback() { 637 @Override 638 public void refreshView(QuickSettingsTileView unused, State state) { 639 locationTile.setImageResource(state.iconId); 640 String locationState = mContext.getString( 641 (state.enabled) ? R.string.accessibility_desc_on 642 : R.string.accessibility_desc_off); 643 locationTile.setContentDescription(mContext.getString( 644 R.string.accessibility_quick_settings_location, 645 locationState)); 646 locationTile.setText(state.label); 647 } 648 }); 649 parent.addView(locationTile); 650 } 651 652 private void addTemporaryTiles(final ViewGroup parent, final LayoutInflater inflater) { 653 // Alarm tile 654 final QuickSettingsBasicTile alarmTile 655 = new QuickSettingsBasicTile(mContext); 656 alarmTile.setImageResource(R.drawable.ic_qs_alarm_on); 657 alarmTile.setOnClickListener(new View.OnClickListener() { 658 @Override 659 public void onClick(View v) { 660 startSettingsActivity(AlarmClock.ACTION_SHOW_ALARMS); 661 } 662 }); 663 mModel.addAlarmTile(alarmTile, new QuickSettingsModel.RefreshCallback() { 664 @Override 665 public void refreshView(QuickSettingsTileView unused, State alarmState) { 666 alarmTile.setText(alarmState.label); 667 alarmTile.setVisibility(alarmState.enabled ? View.VISIBLE : View.GONE); 668 alarmTile.setContentDescription(mContext.getString( 669 R.string.accessibility_quick_settings_alarm, alarmState.label)); 670 } 671 }); 672 parent.addView(alarmTile); 673 674 // Remote Display 675 QuickSettingsBasicTile remoteDisplayTile 676 = new QuickSettingsBasicTile(mContext); 677 remoteDisplayTile.setOnClickListener(new View.OnClickListener() { 678 @Override 679 public void onClick(View v) { 680 collapsePanels(); 681 682 final Dialog[] dialog = new Dialog[1]; 683 dialog[0] = MediaRouteDialogPresenter.createDialog(mContext, 684 MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, 685 new View.OnClickListener() { 686 @Override 687 public void onClick(View v) { 688 dialog[0].dismiss(); 689 startSettingsActivity( 690 android.provider.Settings.ACTION_WIFI_DISPLAY_SETTINGS); 691 } 692 }); 693 dialog[0].getWindow().setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY); 694 dialog[0].show(); 695 } 696 }); 697 mModel.addRemoteDisplayTile(remoteDisplayTile, 698 new QuickSettingsModel.BasicRefreshCallback(remoteDisplayTile) 699 .setShowWhenEnabled(true)); 700 parent.addView(remoteDisplayTile); 701 702 if (SHOW_IME_TILE || DEBUG_GONE_TILES) { 703 // IME 704 final QuickSettingsBasicTile imeTile 705 = new QuickSettingsBasicTile(mContext); 706 imeTile.setImageResource(R.drawable.ic_qs_ime); 707 imeTile.setOnClickListener(new View.OnClickListener() { 708 @Override 709 public void onClick(View v) { 710 try { 711 collapsePanels(); 712 Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER); 713 PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); 714 pendingIntent.send(); 715 } catch (Exception e) {} 716 } 717 }); 718 mModel.addImeTile(imeTile, 719 new QuickSettingsModel.BasicRefreshCallback(imeTile) 720 .setShowWhenEnabled(true)); 721 parent.addView(imeTile); 722 } 723 724 // Bug reports 725 final QuickSettingsBasicTile bugreportTile 726 = new QuickSettingsBasicTile(mContext); 727 bugreportTile.setImageResource(com.android.internal.R.drawable.stat_sys_adb); 728 bugreportTile.setTextResource(com.android.internal.R.string.bugreport_title); 729 bugreportTile.setOnClickListener(new View.OnClickListener() { 730 @Override 731 public void onClick(View v) { 732 collapsePanels(); 733 showBugreportDialog(); 734 } 735 }); 736 mModel.addBugreportTile(bugreportTile, new QuickSettingsModel.RefreshCallback() { 737 @Override 738 public void refreshView(QuickSettingsTileView view, State state) { 739 view.setVisibility(state.enabled ? View.VISIBLE : View.GONE); 740 } 741 }); 742 parent.addView(bugreportTile); 743 /* 744 QuickSettingsTileView mediaTile = (QuickSettingsTileView) 745 inflater.inflate(R.layout.quick_settings_tile, parent, false); 746 mediaTile.setContent(R.layout.quick_settings_tile_media, inflater); 747 parent.addView(mediaTile); 748 QuickSettingsTileView imeTile = (QuickSettingsTileView) 749 inflater.inflate(R.layout.quick_settings_tile, parent, false); 750 imeTile.setContent(R.layout.quick_settings_tile_ime, inflater); 751 imeTile.setOnClickListener(new View.OnClickListener() { 752 @Override 753 public void onClick(View v) { 754 parent.removeViewAt(0); 755 } 756 }); 757 parent.addView(imeTile); 758 */ 759 760 // SSL CA Cert Warning. 761 final QuickSettingsBasicTile sslCaCertWarningTile = 762 new QuickSettingsBasicTile(mContext, null, R.layout.quick_settings_tile_monitoring); 763 sslCaCertWarningTile.setOnClickListener(new View.OnClickListener() { 764 @Override 765 public void onClick(View v) { 766 collapsePanels(); 767 startSettingsActivity(Settings.ACTION_MONITORING_CERT_INFO); 768 } 769 }); 770 771 sslCaCertWarningTile.setImageResource( 772 com.android.internal.R.drawable.indicator_input_error); 773 sslCaCertWarningTile.setTextResource(R.string.ssl_ca_cert_warning); 774 775 mModel.addSslCaCertWarningTile(sslCaCertWarningTile, 776 new QuickSettingsModel.BasicRefreshCallback(sslCaCertWarningTile) 777 .setShowWhenEnabled(true)); 778 parent.addView(sslCaCertWarningTile); 779 } 780 781 void updateResources() { 782 Resources r = mContext.getResources(); 783 784 // Update the model 785 mModel.updateResources(); 786 787 // Update the User, Time, and Settings tiles spans, and reset everything else 788 int span = r.getInteger(R.integer.quick_settings_user_time_settings_tile_span); 789 for (QuickSettingsTileView v : mDynamicSpannedTiles) { 790 v.setColumnSpan(span); 791 } 792 ((QuickSettingsContainerView)mContainerView).updateResources(); 793 mContainerView.requestLayout(); 794 } 795 796 797 private void showBrightnessDialog() { 798 Intent intent = new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG); 799 mContext.sendBroadcast(intent); 800 } 801 802 private void showBugreportDialog() { 803 final AlertDialog.Builder builder = new AlertDialog.Builder(mContext); 804 builder.setPositiveButton(com.android.internal.R.string.report, new OnClickListener() { 805 @Override 806 public void onClick(DialogInterface dialog, int which) { 807 if (which == DialogInterface.BUTTON_POSITIVE) { 808 // Add a little delay before executing, to give the 809 // dialog a chance to go away before it takes a 810 // screenshot. 811 mHandler.postDelayed(new Runnable() { 812 @Override public void run() { 813 try { 814 ActivityManagerNative.getDefault() 815 .requestBugReport(); 816 } catch (RemoteException e) { 817 } 818 } 819 }, 500); 820 } 821 } 822 }); 823 builder.setMessage(com.android.internal.R.string.bugreport_message); 824 builder.setTitle(com.android.internal.R.string.bugreport_title); 825 builder.setCancelable(true); 826 final Dialog dialog = builder.create(); 827 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 828 try { 829 WindowManagerGlobal.getWindowManagerService().dismissKeyguard(); 830 } catch (RemoteException e) { 831 } 832 dialog.show(); 833 } 834 835 private void applyBluetoothStatus() { 836 mModel.onBluetoothStateChange(mBluetoothState); 837 } 838 839 private void applyLocationEnabledStatus() { 840 mModel.onLocationSettingsChanged(mLocationController.isLocationEnabled()); 841 } 842 843 void reloadUserInfo() { 844 if (mUserInfoTask != null) { 845 mUserInfoTask.cancel(false); 846 mUserInfoTask = null; 847 } 848 if (mTilesSetUp) { 849 queryForUserInformation(); 850 queryForSslCaCerts(); 851 } 852 } 853 854 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 855 @Override 856 public void onReceive(Context context, Intent intent) { 857 final String action = intent.getAction(); 858 if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) { 859 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 860 BluetoothAdapter.ERROR); 861 mBluetoothState.enabled = (state == BluetoothAdapter.STATE_ON); 862 applyBluetoothStatus(); 863 } else if (BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED.equals(action)) { 864 int status = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, 865 BluetoothAdapter.STATE_DISCONNECTED); 866 mBluetoothState.connected = (status == BluetoothAdapter.STATE_CONNECTED); 867 applyBluetoothStatus(); 868 } else if (Intent.ACTION_USER_SWITCHED.equals(action)) { 869 reloadUserInfo(); 870 } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { 871 if (mUseDefaultAvatar) { 872 queryForUserInformation(); 873 } 874 } else if (KeyChain.ACTION_STORAGE_CHANGED.equals(action)) { 875 queryForSslCaCerts(); 876 } 877 } 878 }; 879 880 private final BroadcastReceiver mProfileReceiver = new BroadcastReceiver() { 881 @Override 882 public void onReceive(Context context, Intent intent) { 883 final String action = intent.getAction(); 884 if (ContactsContract.Intents.ACTION_PROFILE_CHANGED.equals(action) || 885 Intent.ACTION_USER_INFO_CHANGED.equals(action)) { 886 try { 887 final int currentUser = ActivityManagerNative.getDefault().getCurrentUser().id; 888 final int changedUser = 889 intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId()); 890 if (changedUser == currentUser) { 891 reloadUserInfo(); 892 } 893 } catch (RemoteException e) { 894 Log.e(TAG, "Couldn't get current user id for profile change", e); 895 } 896 } 897 898 } 899 }; 900 901 private abstract static class NetworkActivityCallback 902 implements QuickSettingsModel.RefreshCallback { 903 private final long mDefaultDuration = new ValueAnimator().getDuration(); 904 private final long mShortDuration = mDefaultDuration / 3; 905 906 public void setActivity(View view, ActivityState state) { 907 setVisibility(view.findViewById(R.id.activity_in), state.activityIn); 908 setVisibility(view.findViewById(R.id.activity_out), state.activityOut); 909 } 910 911 private void setVisibility(View view, boolean visible) { 912 final float newAlpha = visible ? 1 : 0; 913 if (view.getAlpha() != newAlpha) { 914 view.animate() 915 .setDuration(visible ? mShortDuration : mDefaultDuration) 916 .alpha(newAlpha) 917 .start(); 918 } 919 } 920 } 921 } 922