KeyguardBottomAreaView.java revision 64d4dca63f65e4c7d4a829c85ff6670bdd34e2eb
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 if (PhoneStatusBar.DEBUG_EMPTY_KEYGUARD) { 132 Log.i(TAG, "Collapsing panel from lock icon accessibility click"); 133 } 134 mPhoneStatusBar.animateCollapsePanels( 135 CommandQueue.FLAG_EXCLUDE_NONE, true /* force */); 136 return true; 137 } else if (host == mCameraImageView) { 138 launchCamera(); 139 return true; 140 } else if (host == mPhoneImageView) { 141 launchPhone(); 142 return true; 143 } 144 } 145 return super.performAccessibilityAction(host, action, args); 146 } 147 }; 148 149 @Override 150 protected void onFinishInflate() { 151 super.onFinishInflate(); 152 mLockPatternUtils = new LockPatternUtils(mContext); 153 mPreviewContainer = (ViewGroup) findViewById(R.id.preview_container); 154 mCameraImageView = (KeyguardAffordanceView) findViewById(R.id.camera_button); 155 mPhoneImageView = (KeyguardAffordanceView) findViewById(R.id.phone_button); 156 mLockIcon = (KeyguardAffordanceView) findViewById(R.id.lock_icon); 157 mIndicationText = (TextView) findViewById(R.id.keyguard_indication_text); 158 watchForCameraPolicyChanges(); 159 updateCameraVisibility(); 160 updatePhoneVisibility(); 161 mUnlockMethodCache = UnlockMethodCache.getInstance(getContext()); 162 mUnlockMethodCache.addListener(this); 163 updateLockIcon(); 164 setClipChildren(false); 165 setClipToPadding(false); 166 mPreviewInflater = new PreviewInflater(mContext, new LockPatternUtils(mContext)); 167 inflatePreviews(); 168 mLockIcon.setOnClickListener(this); 169 mLockIcon.setBackground(mTrustDrawable); 170 mLockIcon.setOnLongClickListener(this); 171 mCameraImageView.setOnClickListener(this); 172 mPhoneImageView.setOnClickListener(this); 173 initAccessibility(); 174 } 175 176 private void initAccessibility() { 177 mLockIcon.setAccessibilityDelegate(mAccessibilityDelegate); 178 mPhoneImageView.setAccessibilityDelegate(mAccessibilityDelegate); 179 mCameraImageView.setAccessibilityDelegate(mAccessibilityDelegate); 180 } 181 182 @Override 183 protected void onConfigurationChanged(Configuration newConfig) { 184 super.onConfigurationChanged(newConfig); 185 int indicationBottomMargin = getResources().getDimensionPixelSize( 186 R.dimen.keyguard_indication_margin_bottom); 187 MarginLayoutParams mlp = (MarginLayoutParams) mIndicationText.getLayoutParams(); 188 if (mlp.bottomMargin != indicationBottomMargin) { 189 mlp.bottomMargin = indicationBottomMargin; 190 mIndicationText.setLayoutParams(mlp); 191 } 192 193 // Respect font size setting. 194 mIndicationText.setTextSize(TypedValue.COMPLEX_UNIT_PX, 195 getResources().getDimensionPixelSize( 196 com.android.internal.R.dimen.text_size_small_material)); 197 } 198 199 public void setActivityStarter(ActivityStarter activityStarter) { 200 mActivityStarter = activityStarter; 201 } 202 203 public void setFlashlightController(FlashlightController flashlightController) { 204 mFlashlightController = flashlightController; 205 } 206 207 public void setAccessibilityController(AccessibilityController accessibilityController) { 208 mAccessibilityController = accessibilityController; 209 accessibilityController.addStateChangedCallback(this); 210 } 211 212 public void setPhoneStatusBar(PhoneStatusBar phoneStatusBar) { 213 mPhoneStatusBar = phoneStatusBar; 214 } 215 216 private Intent getCameraIntent() { 217 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); 218 boolean currentUserHasTrust = updateMonitor.getUserHasTrust( 219 mLockPatternUtils.getCurrentUser()); 220 return mLockPatternUtils.isSecure() && !currentUserHasTrust 221 ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT; 222 } 223 224 private void updateCameraVisibility() { 225 ResolveInfo resolved = mContext.getPackageManager().resolveActivityAsUser(getCameraIntent(), 226 PackageManager.MATCH_DEFAULT_ONLY, 227 mLockPatternUtils.getCurrentUser()); 228 boolean visible = !isCameraDisabledByDpm() && resolved != null 229 && getResources().getBoolean(R.bool.config_keyguardShowCameraAffordance); 230 mCameraImageView.setVisibility(visible ? View.VISIBLE : View.GONE); 231 } 232 233 private void updatePhoneVisibility() { 234 boolean visible = isPhoneVisible(); 235 mPhoneImageView.setVisibility(visible ? View.VISIBLE : View.GONE); 236 } 237 238 private boolean isPhoneVisible() { 239 PackageManager pm = mContext.getPackageManager(); 240 return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) 241 && pm.resolveActivity(PHONE_INTENT, 0) != null; 242 } 243 244 private boolean isCameraDisabledByDpm() { 245 final DevicePolicyManager dpm = 246 (DevicePolicyManager) getContext().getSystemService(Context.DEVICE_POLICY_SERVICE); 247 if (dpm != null) { 248 try { 249 final int userId = ActivityManagerNative.getDefault().getCurrentUser().id; 250 final int disabledFlags = dpm.getKeyguardDisabledFeatures(null, userId); 251 final boolean disabledBecauseKeyguardSecure = 252 (disabledFlags & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0 253 && KeyguardTouchDelegate.getInstance(getContext()).isSecure(); 254 return dpm.getCameraDisabled(null) || disabledBecauseKeyguardSecure; 255 } catch (RemoteException e) { 256 Log.e(TAG, "Can't get userId", e); 257 } 258 } 259 return false; 260 } 261 262 private void watchForCameraPolicyChanges() { 263 final IntentFilter filter = new IntentFilter(); 264 filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); 265 getContext().registerReceiverAsUser(mDevicePolicyReceiver, 266 UserHandle.ALL, filter, null, null); 267 KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback); 268 } 269 270 @Override 271 public void onStateChanged(boolean accessibilityEnabled, boolean touchExplorationEnabled) { 272 mCameraImageView.setClickable(touchExplorationEnabled); 273 mPhoneImageView.setClickable(touchExplorationEnabled); 274 mCameraImageView.setFocusable(accessibilityEnabled); 275 mPhoneImageView.setFocusable(accessibilityEnabled); 276 updateLockIconClickability(); 277 } 278 279 private void updateLockIconClickability() { 280 if (mAccessibilityController == null) { 281 return; 282 } 283 mLockIcon.setClickable(mUnlockMethodCache.isTrustManaged() 284 || mAccessibilityController.isTouchExplorationEnabled()); 285 mLockIcon.setLongClickable(mAccessibilityController.isTouchExplorationEnabled() 286 && mUnlockMethodCache.isTrustManaged()); 287 mLockIcon.setFocusable(mAccessibilityController.isAccessibilityEnabled()); 288 } 289 290 @Override 291 public void onClick(View v) { 292 if (v == mCameraImageView) { 293 launchCamera(); 294 } else if (v == mPhoneImageView) { 295 launchPhone(); 296 } if (v == mLockIcon) { 297 if (!mAccessibilityController.isAccessibilityEnabled()) { 298 handleTrustCircleClick(); 299 } else { 300 mPhoneStatusBar.animateCollapsePanels( 301 CommandQueue.FLAG_EXCLUDE_NONE, true /* force */); 302 } 303 } 304 } 305 306 @Override 307 public boolean onLongClick(View v) { 308 handleTrustCircleClick(); 309 return true; 310 } 311 312 private void handleTrustCircleClick() { 313 mIndicationController.showTransientIndication( 314 R.string.keyguard_indication_trust_disabled); 315 mLockPatternUtils.requireCredentialEntry(mLockPatternUtils.getCurrentUser()); 316 } 317 318 public void launchCamera() { 319 mFlashlightController.killFlashlight(); 320 Intent intent = getCameraIntent(); 321 boolean wouldLaunchResolverActivity = PreviewInflater.wouldLaunchResolverActivity( 322 mContext, intent, mLockPatternUtils.getCurrentUser()); 323 if (intent == SECURE_CAMERA_INTENT && !wouldLaunchResolverActivity) { 324 mContext.startActivityAsUser(intent, UserHandle.CURRENT); 325 } else { 326 327 // We need to delay starting the activity because ResolverActivity finishes itself if 328 // launched behind lockscreen. 329 mActivityStarter.startActivity(intent, false /* dismissShade */); 330 } 331 } 332 333 public void launchPhone() { 334 final TelecommManager tm = TelecommManager.from(mContext); 335 if (tm.isInCall()) { 336 AsyncTask.execute(new Runnable() { 337 @Override 338 public void run() { 339 tm.showInCallScreen(false /* showDialpad */); 340 } 341 }); 342 } else { 343 mActivityStarter.startActivity(PHONE_INTENT, false /* dismissShade */); 344 } 345 } 346 347 348 @Override 349 protected void onVisibilityChanged(View changedView, int visibility) { 350 super.onVisibilityChanged(changedView, visibility); 351 if (isShown()) { 352 mTrustDrawable.start(); 353 } else { 354 mTrustDrawable.stop(); 355 } 356 if (changedView == this && visibility == VISIBLE) { 357 updateLockIcon(); 358 updateCameraVisibility(); 359 } 360 } 361 362 @Override 363 protected void onDetachedFromWindow() { 364 super.onDetachedFromWindow(); 365 mTrustDrawable.stop(); 366 } 367 368 private void updateLockIcon() { 369 boolean visible = isShown() && KeyguardUpdateMonitor.getInstance(mContext).isScreenOn(); 370 if (visible) { 371 mTrustDrawable.start(); 372 } else { 373 mTrustDrawable.stop(); 374 } 375 if (!visible) { 376 return; 377 } 378 // TODO: Real icon for facelock. 379 int iconRes = mUnlockMethodCache.isFaceUnlockRunning() 380 ? com.android.internal.R.drawable.ic_account_circle 381 : mUnlockMethodCache.isMethodInsecure() ? R.drawable.ic_lock_open_24dp 382 : R.drawable.ic_lock_24dp; 383 mLockIcon.setImageResource(iconRes); 384 boolean trustManaged = mUnlockMethodCache.isTrustManaged(); 385 mTrustDrawable.setTrustManaged(trustManaged); 386 updateLockIconClickability(); 387 } 388 389 390 391 public KeyguardAffordanceView getPhoneView() { 392 return mPhoneImageView; 393 } 394 395 public KeyguardAffordanceView getCameraView() { 396 return mCameraImageView; 397 } 398 399 public View getPhonePreview() { 400 return mPhonePreview; 401 } 402 403 public View getCameraPreview() { 404 return mCameraPreview; 405 } 406 407 public KeyguardAffordanceView getLockIcon() { 408 return mLockIcon; 409 } 410 411 public View getIndicationView() { 412 return mIndicationText; 413 } 414 415 @Override 416 public boolean hasOverlappingRendering() { 417 return false; 418 } 419 420 @Override 421 public void onMethodSecureChanged(boolean methodSecure) { 422 updateLockIcon(); 423 updateCameraVisibility(); 424 } 425 426 private void inflatePreviews() { 427 mPhonePreview = mPreviewInflater.inflatePreview(PHONE_INTENT); 428 mCameraPreview = mPreviewInflater.inflatePreview(getCameraIntent()); 429 if (mPhonePreview != null) { 430 mPreviewContainer.addView(mPhonePreview); 431 mPhonePreview.setVisibility(View.INVISIBLE); 432 } 433 if (mCameraPreview != null) { 434 mPreviewContainer.addView(mCameraPreview); 435 mCameraPreview.setVisibility(View.INVISIBLE); 436 } 437 } 438 439 private final BroadcastReceiver mDevicePolicyReceiver = new BroadcastReceiver() { 440 public void onReceive(Context context, Intent intent) { 441 post(new Runnable() { 442 @Override 443 public void run() { 444 updateCameraVisibility(); 445 } 446 }); 447 } 448 }; 449 450 private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback = 451 new KeyguardUpdateMonitorCallback() { 452 @Override 453 public void onUserSwitchComplete(int userId) { 454 updateCameraVisibility(); 455 } 456 457 @Override 458 public void onScreenTurnedOn() { 459 updateLockIcon(); 460 } 461 462 @Override 463 public void onScreenTurnedOff(int why) { 464 updateLockIcon(); 465 } 466 }; 467 468 public void setKeyguardIndicationController( 469 KeyguardIndicationController keyguardIndicationController) { 470 mIndicationController = keyguardIndicationController; 471 } 472} 473