KeyguardBottomAreaView.java revision baef32faa544ac60de6bda527a9ec343f068f5a0
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.statusbar.phone; 18 19import android.app.ActivityManagerNative; 20import android.app.admin.DevicePolicyManager; 21import android.content.BroadcastReceiver; 22import android.content.Context; 23import android.content.Intent; 24import android.content.IntentFilter; 25import android.content.pm.PackageManager; 26import android.content.pm.ResolveInfo; 27import android.content.res.Configuration; 28import android.os.AsyncTask; 29import android.os.Bundle; 30import android.os.RemoteException; 31import android.os.UserHandle; 32import android.phone.PhoneManager; 33import android.provider.MediaStore; 34import android.telecomm.TelecommManager; 35import android.util.AttributeSet; 36import android.util.Log; 37import android.util.TypedValue; 38import android.view.View; 39import android.view.ViewGroup; 40import android.view.accessibility.AccessibilityNodeInfo; 41import android.widget.FrameLayout; 42import android.widget.TextView; 43 44import com.android.internal.widget.LockPatternUtils; 45import com.android.keyguard.KeyguardUpdateMonitor; 46import com.android.keyguard.KeyguardUpdateMonitorCallback; 47import com.android.systemui.R; 48import com.android.systemui.statusbar.CommandQueue; 49import com.android.systemui.statusbar.KeyguardAffordanceView; 50import com.android.systemui.statusbar.KeyguardIndicationController; 51import com.android.systemui.statusbar.policy.AccessibilityController; 52import com.android.systemui.statusbar.policy.FlashlightController; 53import com.android.systemui.statusbar.policy.PreviewInflater; 54 55import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; 56import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; 57 58/** 59 * Implementation for the bottom area of the Keyguard, including camera/phone affordance and status 60 * text. 61 */ 62public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickListener, 63 UnlockMethodCache.OnUnlockMethodChangedListener, 64 AccessibilityController.AccessibilityStateChangedCallback, View.OnLongClickListener { 65 66 final static String TAG = "PhoneStatusBar/KeyguardBottomAreaView"; 67 68 private static final Intent SECURE_CAMERA_INTENT = 69 new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE) 70 .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 71 private static final Intent INSECURE_CAMERA_INTENT = 72 new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA); 73 private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL); 74 75 private KeyguardAffordanceView mCameraImageView; 76 private KeyguardAffordanceView mPhoneImageView; 77 private KeyguardAffordanceView mLockIcon; 78 private TextView mIndicationText; 79 private ViewGroup mPreviewContainer; 80 81 private View mPhonePreview; 82 private View mCameraPreview; 83 84 private ActivityStarter mActivityStarter; 85 private UnlockMethodCache mUnlockMethodCache; 86 private LockPatternUtils mLockPatternUtils; 87 private FlashlightController mFlashlightController; 88 private PreviewInflater mPreviewInflater; 89 private KeyguardIndicationController mIndicationController; 90 private AccessibilityController mAccessibilityController; 91 private PhoneStatusBar mPhoneStatusBar; 92 93 private final TrustDrawable mTrustDrawable; 94 95 public KeyguardBottomAreaView(Context context) { 96 this(context, null); 97 } 98 99 public KeyguardBottomAreaView(Context context, AttributeSet attrs) { 100 this(context, attrs, 0); 101 } 102 103 public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr) { 104 this(context, attrs, defStyleAttr, 0); 105 } 106 107 public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr, 108 int defStyleRes) { 109 super(context, attrs, defStyleAttr, defStyleRes); 110 mTrustDrawable = new TrustDrawable(mContext); 111 } 112 113 private AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() { 114 @Override 115 public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { 116 super.onInitializeAccessibilityNodeInfo(host, info); 117 String label = null; 118 if (host == mLockIcon) { 119 label = getResources().getString(R.string.unlock_label); 120 } else if (host == mCameraImageView) { 121 label = getResources().getString(R.string.camera_label); 122 } else if (host == mPhoneImageView) { 123 label = getResources().getString(R.string.phone_label); 124 } 125 info.addAction(new AccessibilityAction(ACTION_CLICK, label)); 126 } 127 128 @Override 129 public boolean performAccessibilityAction(View host, int action, Bundle args) { 130 if (action == ACTION_CLICK) { 131 if (host == mLockIcon) { 132 mPhoneStatusBar.animateCollapsePanels( 133 CommandQueue.FLAG_EXCLUDE_NONE, true /* force */); 134 return true; 135 } else if (host == mCameraImageView) { 136 launchCamera(); 137 return true; 138 } else if (host == mPhoneImageView) { 139 launchPhone(); 140 return true; 141 } 142 } 143 return super.performAccessibilityAction(host, action, args); 144 } 145 }; 146 147 @Override 148 protected void onFinishInflate() { 149 super.onFinishInflate(); 150 mLockPatternUtils = new LockPatternUtils(mContext); 151 mPreviewContainer = (ViewGroup) findViewById(R.id.preview_container); 152 mCameraImageView = (KeyguardAffordanceView) findViewById(R.id.camera_button); 153 mPhoneImageView = (KeyguardAffordanceView) findViewById(R.id.phone_button); 154 mLockIcon = (KeyguardAffordanceView) findViewById(R.id.lock_icon); 155 mIndicationText = (TextView) findViewById(R.id.keyguard_indication_text); 156 watchForCameraPolicyChanges(); 157 updateCameraVisibility(); 158 updatePhoneVisibility(); 159 mUnlockMethodCache = UnlockMethodCache.getInstance(getContext()); 160 mUnlockMethodCache.addListener(this); 161 updateLockIcon(); 162 setClipChildren(false); 163 setClipToPadding(false); 164 mPreviewInflater = new PreviewInflater(mContext, new LockPatternUtils(mContext)); 165 inflatePreviews(); 166 mLockIcon.setOnClickListener(this); 167 mLockIcon.setBackground(mTrustDrawable); 168 mLockIcon.setOnLongClickListener(this); 169 mCameraImageView.setOnClickListener(this); 170 mPhoneImageView.setOnClickListener(this); 171 initAccessibility(); 172 } 173 174 private void initAccessibility() { 175 mLockIcon.setAccessibilityDelegate(mAccessibilityDelegate); 176 mPhoneImageView.setAccessibilityDelegate(mAccessibilityDelegate); 177 mCameraImageView.setAccessibilityDelegate(mAccessibilityDelegate); 178 } 179 180 @Override 181 protected void onConfigurationChanged(Configuration newConfig) { 182 super.onConfigurationChanged(newConfig); 183 int indicationBottomMargin = getResources().getDimensionPixelSize( 184 R.dimen.keyguard_indication_margin_bottom); 185 MarginLayoutParams mlp = (MarginLayoutParams) mIndicationText.getLayoutParams(); 186 if (mlp.bottomMargin != indicationBottomMargin) { 187 mlp.bottomMargin = indicationBottomMargin; 188 mIndicationText.setLayoutParams(mlp); 189 } 190 191 // Respect font size setting. 192 mIndicationText.setTextSize(TypedValue.COMPLEX_UNIT_PX, 193 getResources().getDimensionPixelSize( 194 com.android.internal.R.dimen.text_size_small_material)); 195 } 196 197 public void setActivityStarter(ActivityStarter activityStarter) { 198 mActivityStarter = activityStarter; 199 } 200 201 public void setFlashlightController(FlashlightController flashlightController) { 202 mFlashlightController = flashlightController; 203 } 204 205 public void setAccessibilityController(AccessibilityController accessibilityController) { 206 mAccessibilityController = accessibilityController; 207 accessibilityController.addStateChangedCallback(this); 208 } 209 210 public void setPhoneStatusBar(PhoneStatusBar phoneStatusBar) { 211 mPhoneStatusBar = phoneStatusBar; 212 } 213 214 private Intent getCameraIntent() { 215 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); 216 boolean currentUserHasTrust = updateMonitor.getUserHasTrust( 217 mLockPatternUtils.getCurrentUser()); 218 return mLockPatternUtils.isSecure() && !currentUserHasTrust 219 ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT; 220 } 221 222 private void updateCameraVisibility() { 223 ResolveInfo resolved = mContext.getPackageManager().resolveActivityAsUser(getCameraIntent(), 224 PackageManager.MATCH_DEFAULT_ONLY, 225 mLockPatternUtils.getCurrentUser()); 226 boolean visible = !isCameraDisabledByDpm() && resolved != null; 227 mCameraImageView.setVisibility(visible ? View.VISIBLE : View.GONE); 228 } 229 230 private void updatePhoneVisibility() { 231 boolean visible = isPhoneVisible(); 232 mPhoneImageView.setVisibility(visible ? View.VISIBLE : View.GONE); 233 } 234 235 private boolean isPhoneVisible() { 236 PackageManager pm = mContext.getPackageManager(); 237 return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) 238 && pm.resolveActivity(PHONE_INTENT, 0) != null; 239 } 240 241 private boolean isCameraDisabledByDpm() { 242 final DevicePolicyManager dpm = 243 (DevicePolicyManager) getContext().getSystemService(Context.DEVICE_POLICY_SERVICE); 244 if (dpm != null) { 245 try { 246 final int userId = ActivityManagerNative.getDefault().getCurrentUser().id; 247 final int disabledFlags = dpm.getKeyguardDisabledFeatures(null, userId); 248 final boolean disabledBecauseKeyguardSecure = 249 (disabledFlags & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0 250 && KeyguardTouchDelegate.getInstance(getContext()).isSecure(); 251 return dpm.getCameraDisabled(null) || disabledBecauseKeyguardSecure; 252 } catch (RemoteException e) { 253 Log.e(TAG, "Can't get userId", e); 254 } 255 } 256 return false; 257 } 258 259 private void watchForCameraPolicyChanges() { 260 final IntentFilter filter = new IntentFilter(); 261 filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); 262 getContext().registerReceiverAsUser(mDevicePolicyReceiver, 263 UserHandle.ALL, filter, null, null); 264 KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback); 265 } 266 267 @Override 268 public void onStateChanged(boolean accessibilityEnabled, boolean touchExplorationEnabled) { 269 mCameraImageView.setClickable(touchExplorationEnabled); 270 mPhoneImageView.setClickable(touchExplorationEnabled); 271 mCameraImageView.setFocusable(accessibilityEnabled); 272 mPhoneImageView.setFocusable(accessibilityEnabled); 273 updateLockIconClickability(); 274 } 275 276 private void updateLockIconClickability() { 277 if (mAccessibilityController == null) { 278 return; 279 } 280 mLockIcon.setClickable(mUnlockMethodCache.isTrustManaged() 281 || mAccessibilityController.isTouchExplorationEnabled()); 282 mLockIcon.setLongClickable(mAccessibilityController.isTouchExplorationEnabled() 283 && mUnlockMethodCache.isTrustManaged()); 284 mLockIcon.setFocusable(mAccessibilityController.isAccessibilityEnabled()); 285 } 286 287 @Override 288 public void onClick(View v) { 289 if (v == mCameraImageView) { 290 launchCamera(); 291 } else if (v == mPhoneImageView) { 292 launchPhone(); 293 } if (v == mLockIcon) { 294 if (!mAccessibilityController.isAccessibilityEnabled()) { 295 handleTrustCircleClick(); 296 } else { 297 mPhoneStatusBar.animateCollapsePanels( 298 CommandQueue.FLAG_EXCLUDE_NONE, true /* force */); 299 } 300 } 301 } 302 303 @Override 304 public boolean onLongClick(View v) { 305 handleTrustCircleClick(); 306 return true; 307 } 308 309 private void handleTrustCircleClick() { 310 mIndicationController.showTransientIndication( 311 R.string.keyguard_indication_trust_disabled); 312 mLockPatternUtils.requireCredentialEntry(mLockPatternUtils.getCurrentUser()); 313 } 314 315 public void launchCamera() { 316 mFlashlightController.killFlashlight(); 317 Intent intent = getCameraIntent(); 318 if (intent == SECURE_CAMERA_INTENT && 319 !mPreviewInflater.wouldLaunchResolverActivity(intent)) { 320 mContext.startActivityAsUser(intent, UserHandle.CURRENT); 321 } else { 322 mActivityStarter.startActivity(intent, false /* dismissShade */); 323 } 324 } 325 326 public void launchPhone() { 327 TelecommManager tm = TelecommManager.from(mContext); 328 if (tm.isInAPhoneCall()) { 329 final PhoneManager pm = (PhoneManager) mContext.getSystemService(Context.PHONE_SERVICE); 330 AsyncTask.execute(new Runnable() { 331 @Override 332 public void run() { 333 pm.showCallScreen(false /* showDialpad */); 334 } 335 }); 336 } else { 337 mActivityStarter.startActivity(PHONE_INTENT, false /* dismissShade */); 338 } 339 } 340 341 342 @Override 343 protected void onVisibilityChanged(View changedView, int visibility) { 344 super.onVisibilityChanged(changedView, visibility); 345 if (isShown()) { 346 mTrustDrawable.start(); 347 } else { 348 mTrustDrawable.stop(); 349 } 350 if (changedView == this && visibility == VISIBLE) { 351 updateLockIcon(); 352 updateCameraVisibility(); 353 } 354 } 355 356 @Override 357 protected void onDetachedFromWindow() { 358 super.onDetachedFromWindow(); 359 mTrustDrawable.stop(); 360 } 361 362 private void updateLockIcon() { 363 boolean visible = isShown() && KeyguardUpdateMonitor.getInstance(mContext).isScreenOn(); 364 if (visible) { 365 mTrustDrawable.start(); 366 } else { 367 mTrustDrawable.stop(); 368 } 369 if (!visible) { 370 return; 371 } 372 // TODO: Real icon for facelock. 373 int iconRes = mUnlockMethodCache.isFaceUnlockRunning() ? R.drawable.ic_account_circle 374 : mUnlockMethodCache.isMethodInsecure() ? R.drawable.ic_lock_open_24dp 375 : R.drawable.ic_lock_24dp; 376 mLockIcon.setImageResource(iconRes); 377 boolean trustManaged = mUnlockMethodCache.isTrustManaged(); 378 mTrustDrawable.setTrustManaged(trustManaged); 379 380 updateLockIconClickability(); 381 updateLockIconContentDescription(mUnlockMethodCache.isFaceUnlockRunning(), 382 mUnlockMethodCache.isMethodInsecure(), trustManaged); 383 } 384 385 private void updateLockIconContentDescription(boolean faceUnlockRunning, boolean insecure, 386 boolean trustManaged) { 387 mLockIcon.setContentDescription(getResources().getString( 388 faceUnlockRunning ? R.string.accessibility_unlock_button_face_unlock_running 389 : insecure && !trustManaged ? R.string.accessibility_unlock_button_not_secured 390 : insecure ? R.string.accessibility_unlock_button_not_secured_trust_managed 391 : !trustManaged ? R.string.accessibility_unlock_button_secured 392 : R.string.accessibility_unlock_button_secured_trust_managed)); 393 } 394 395 public KeyguardAffordanceView getPhoneView() { 396 return mPhoneImageView; 397 } 398 399 public KeyguardAffordanceView getCameraView() { 400 return mCameraImageView; 401 } 402 403 public View getPhonePreview() { 404 return mPhonePreview; 405 } 406 407 public View getCameraPreview() { 408 return mCameraPreview; 409 } 410 411 public KeyguardAffordanceView getLockIcon() { 412 return mLockIcon; 413 } 414 415 public View getIndicationView() { 416 return mIndicationText; 417 } 418 419 @Override 420 public boolean hasOverlappingRendering() { 421 return false; 422 } 423 424 @Override 425 public void onMethodSecureChanged(boolean methodSecure) { 426 updateLockIcon(); 427 updateCameraVisibility(); 428 } 429 430 private void inflatePreviews() { 431 mPhonePreview = mPreviewInflater.inflatePreview(PHONE_INTENT); 432 mCameraPreview = mPreviewInflater.inflatePreview(getCameraIntent()); 433 if (mPhonePreview != null) { 434 mPreviewContainer.addView(mPhonePreview); 435 mPhonePreview.setVisibility(View.INVISIBLE); 436 } 437 if (mCameraPreview != null) { 438 mPreviewContainer.addView(mCameraPreview); 439 mCameraPreview.setVisibility(View.INVISIBLE); 440 } 441 } 442 443 private final BroadcastReceiver mDevicePolicyReceiver = new BroadcastReceiver() { 444 public void onReceive(Context context, Intent intent) { 445 post(new Runnable() { 446 @Override 447 public void run() { 448 updateCameraVisibility(); 449 } 450 }); 451 } 452 }; 453 454 private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback = 455 new KeyguardUpdateMonitorCallback() { 456 @Override 457 public void onUserSwitchComplete(int userId) { 458 updateCameraVisibility(); 459 } 460 461 @Override 462 public void onScreenTurnedOn() { 463 updateLockIcon(); 464 } 465 466 @Override 467 public void onScreenTurnedOff(int why) { 468 updateLockIcon(); 469 } 470 }; 471 472 public void setKeyguardIndicationController( 473 KeyguardIndicationController keyguardIndicationController) { 474 mIndicationController = keyguardIndicationController; 475 } 476} 477