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