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 17 package com.android.systemui.statusbar.phone; 18 19 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; 20 import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; 21 22 import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_BUTTON; 23 import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_UNLOCK; 24 import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_BUTTON; 25 import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_UNLOCK; 26 27 import android.app.ActivityManager; 28 import android.app.ActivityOptions; 29 import android.app.admin.DevicePolicyManager; 30 import android.content.BroadcastReceiver; 31 import android.content.ComponentName; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.content.IntentFilter; 35 import android.content.ServiceConnection; 36 import android.content.pm.ActivityInfo; 37 import android.content.pm.PackageManager; 38 import android.content.pm.ResolveInfo; 39 import android.content.res.Configuration; 40 import android.graphics.drawable.Drawable; 41 import android.os.AsyncTask; 42 import android.os.Bundle; 43 import android.os.IBinder; 44 import android.os.Message; 45 import android.os.Messenger; 46 import android.os.RemoteException; 47 import android.os.UserHandle; 48 import android.provider.MediaStore; 49 import android.service.media.CameraPrewarmService; 50 import android.telecom.TelecomManager; 51 import android.text.TextUtils; 52 import android.util.AttributeSet; 53 import android.util.Log; 54 import android.util.MathUtils; 55 import android.util.TypedValue; 56 import android.view.View; 57 import android.view.ViewGroup; 58 import android.view.WindowInsets; 59 import android.view.WindowManager; 60 import android.view.accessibility.AccessibilityNodeInfo; 61 import android.widget.FrameLayout; 62 import android.widget.TextView; 63 64 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 65 import com.android.internal.widget.LockPatternUtils; 66 import com.android.keyguard.KeyguardUpdateMonitor; 67 import com.android.keyguard.KeyguardUpdateMonitorCallback; 68 import com.android.systemui.EventLogTags; 69 import com.android.systemui.Dependency; 70 import com.android.systemui.Interpolators; 71 import com.android.systemui.R; 72 import com.android.systemui.assist.AssistManager; 73 import com.android.systemui.plugins.IntentButtonProvider; 74 import com.android.systemui.plugins.IntentButtonProvider.IntentButton; 75 import com.android.systemui.plugins.IntentButtonProvider.IntentButton.IconState; 76 import com.android.systemui.plugins.PluginListener; 77 import com.android.systemui.plugins.ActivityStarter; 78 import com.android.systemui.statusbar.CommandQueue; 79 import com.android.systemui.statusbar.KeyguardAffordanceView; 80 import com.android.systemui.statusbar.KeyguardIndicationController; 81 import com.android.systemui.statusbar.policy.AccessibilityController; 82 import com.android.systemui.statusbar.policy.ExtensionController; 83 import com.android.systemui.statusbar.policy.ExtensionController.Extension; 84 import com.android.systemui.statusbar.policy.FlashlightController; 85 import com.android.systemui.statusbar.policy.PreviewInflater; 86 import com.android.systemui.tuner.LockscreenFragment.LockButtonFactory; 87 import com.android.systemui.tuner.TunerService; 88 89 /** 90 * Implementation for the bottom area of the Keyguard, including camera/phone affordance and status 91 * text. 92 */ 93 public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickListener, 94 UnlockMethodCache.OnUnlockMethodChangedListener, 95 AccessibilityController.AccessibilityStateChangedCallback, View.OnLongClickListener { 96 97 final static String TAG = "StatusBar/KeyguardBottomAreaView"; 98 99 public static final String CAMERA_LAUNCH_SOURCE_AFFORDANCE = "lockscreen_affordance"; 100 public static final String CAMERA_LAUNCH_SOURCE_WIGGLE = "wiggle_gesture"; 101 public static final String CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP = "power_double_tap"; 102 public static final String CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER = "lift_to_launch_ml"; 103 104 public static final String EXTRA_CAMERA_LAUNCH_SOURCE 105 = "com.android.systemui.camera_launch_source"; 106 107 private static final String LEFT_BUTTON_PLUGIN 108 = "com.android.systemui.action.PLUGIN_LOCKSCREEN_LEFT_BUTTON"; 109 private static final String RIGHT_BUTTON_PLUGIN 110 = "com.android.systemui.action.PLUGIN_LOCKSCREEN_RIGHT_BUTTON"; 111 112 private static final Intent SECURE_CAMERA_INTENT = 113 new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE) 114 .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 115 public static final Intent INSECURE_CAMERA_INTENT = 116 new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA); 117 private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL); 118 private static final int DOZE_ANIMATION_STAGGER_DELAY = 48; 119 private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250; 120 121 private KeyguardAffordanceView mRightAffordanceView; 122 private KeyguardAffordanceView mLeftAffordanceView; 123 private LockIcon mLockIcon; 124 private ViewGroup mIndicationArea; 125 private TextView mEnterpriseDisclosure; 126 private TextView mIndicationText; 127 private ViewGroup mPreviewContainer; 128 private ViewGroup mOverlayContainer; 129 130 private View mLeftPreview; 131 private View mCameraPreview; 132 133 private ActivityStarter mActivityStarter; 134 private UnlockMethodCache mUnlockMethodCache; 135 private LockPatternUtils mLockPatternUtils; 136 private FlashlightController mFlashlightController; 137 private PreviewInflater mPreviewInflater; 138 private KeyguardIndicationController mIndicationController; 139 private AccessibilityController mAccessibilityController; 140 private StatusBar mStatusBar; 141 private KeyguardAffordanceHelper mAffordanceHelper; 142 143 private boolean mUserSetupComplete; 144 private boolean mPrewarmBound; 145 private Messenger mPrewarmMessenger; 146 private final ServiceConnection mPrewarmConnection = new ServiceConnection() { 147 148 @Override 149 public void onServiceConnected(ComponentName name, IBinder service) { 150 mPrewarmMessenger = new Messenger(service); 151 } 152 153 @Override 154 public void onServiceDisconnected(ComponentName name) { 155 mPrewarmMessenger = null; 156 } 157 }; 158 159 private boolean mLeftIsVoiceAssist; 160 private AssistManager mAssistManager; 161 private Drawable mLeftAssistIcon; 162 163 private IntentButton mRightButton = new DefaultRightButton(); 164 private Extension<IntentButton> mRightExtension; 165 private String mRightButtonStr; 166 private IntentButton mLeftButton = new DefaultLeftButton(); 167 private Extension<IntentButton> mLeftExtension; 168 private String mLeftButtonStr; 169 private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger(); 170 private boolean mDozing; 171 private int mIndicationBottomMargin; 172 private int mIndicationBottomMarginAmbient; 173 private float mDarkAmount; 174 private int mBurnInXOffset; 175 KeyguardBottomAreaView(Context context)176 public KeyguardBottomAreaView(Context context) { 177 this(context, null); 178 } 179 KeyguardBottomAreaView(Context context, AttributeSet attrs)180 public KeyguardBottomAreaView(Context context, AttributeSet attrs) { 181 this(context, attrs, 0); 182 } 183 KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr)184 public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr) { 185 this(context, attrs, defStyleAttr, 0); 186 } 187 KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)188 public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr, 189 int defStyleRes) { 190 super(context, attrs, defStyleAttr, defStyleRes); 191 } 192 193 private AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() { 194 @Override 195 public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { 196 super.onInitializeAccessibilityNodeInfo(host, info); 197 String label = null; 198 if (host == mLockIcon) { 199 label = getResources().getString(R.string.unlock_label); 200 } else if (host == mRightAffordanceView) { 201 label = getResources().getString(R.string.camera_label); 202 } else if (host == mLeftAffordanceView) { 203 if (mLeftIsVoiceAssist) { 204 label = getResources().getString(R.string.voice_assist_label); 205 } else { 206 label = getResources().getString(R.string.phone_label); 207 } 208 } 209 info.addAction(new AccessibilityAction(ACTION_CLICK, label)); 210 } 211 212 @Override 213 public boolean performAccessibilityAction(View host, int action, Bundle args) { 214 if (action == ACTION_CLICK) { 215 if (host == mLockIcon) { 216 mStatusBar.animateCollapsePanels( 217 CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */); 218 return true; 219 } else if (host == mRightAffordanceView) { 220 launchCamera(CAMERA_LAUNCH_SOURCE_AFFORDANCE); 221 return true; 222 } else if (host == mLeftAffordanceView) { 223 launchLeftAffordance(); 224 return true; 225 } 226 } 227 return super.performAccessibilityAction(host, action, args); 228 } 229 }; 230 231 @Override onFinishInflate()232 protected void onFinishInflate() { 233 super.onFinishInflate(); 234 mLockPatternUtils = new LockPatternUtils(mContext); 235 mPreviewContainer = findViewById(R.id.preview_container); 236 mOverlayContainer = findViewById(R.id.overlay_container); 237 mRightAffordanceView = findViewById(R.id.camera_button); 238 mLeftAffordanceView = findViewById(R.id.left_button); 239 mLockIcon = findViewById(R.id.lock_icon); 240 mIndicationArea = findViewById(R.id.keyguard_indication_area); 241 mEnterpriseDisclosure = findViewById( 242 R.id.keyguard_indication_enterprise_disclosure); 243 mIndicationText = findViewById(R.id.keyguard_indication_text); 244 mIndicationBottomMargin = getResources().getDimensionPixelSize( 245 R.dimen.keyguard_indication_margin_bottom); 246 mIndicationBottomMarginAmbient = getResources().getDimensionPixelSize( 247 R.dimen.keyguard_indication_margin_bottom_ambient); 248 updateCameraVisibility(); 249 mUnlockMethodCache = UnlockMethodCache.getInstance(getContext()); 250 mUnlockMethodCache.addListener(this); 251 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); 252 mLockIcon.setScreenOn(updateMonitor.isScreenOn()); 253 mLockIcon.setDeviceInteractive(updateMonitor.isDeviceInteractive()); 254 mLockIcon.update(); 255 setClipChildren(false); 256 setClipToPadding(false); 257 mPreviewInflater = new PreviewInflater(mContext, new LockPatternUtils(mContext)); 258 inflateCameraPreview(); 259 mLockIcon.setOnClickListener(this); 260 mLockIcon.setOnLongClickListener(this); 261 mRightAffordanceView.setOnClickListener(this); 262 mLeftAffordanceView.setOnClickListener(this); 263 initAccessibility(); 264 mActivityStarter = Dependency.get(ActivityStarter.class); 265 mFlashlightController = Dependency.get(FlashlightController.class); 266 mAccessibilityController = Dependency.get(AccessibilityController.class); 267 mAssistManager = Dependency.get(AssistManager.class); 268 mLockIcon.setAccessibilityController(mAccessibilityController); 269 updateLeftAffordance(); 270 } 271 272 @Override onAttachedToWindow()273 protected void onAttachedToWindow() { 274 super.onAttachedToWindow(); 275 mAccessibilityController.addStateChangedCallback(this); 276 mRightExtension = Dependency.get(ExtensionController.class).newExtension(IntentButton.class) 277 .withPlugin(IntentButtonProvider.class, RIGHT_BUTTON_PLUGIN, 278 p -> p.getIntentButton()) 279 .withTunerFactory(new LockButtonFactory(mContext, LOCKSCREEN_RIGHT_BUTTON)) 280 .withDefault(() -> new DefaultRightButton()) 281 .withCallback(button -> setRightButton(button)) 282 .build(); 283 mLeftExtension = Dependency.get(ExtensionController.class).newExtension(IntentButton.class) 284 .withPlugin(IntentButtonProvider.class, LEFT_BUTTON_PLUGIN, 285 p -> p.getIntentButton()) 286 .withTunerFactory(new LockButtonFactory(mContext, LOCKSCREEN_LEFT_BUTTON)) 287 .withDefault(() -> new DefaultLeftButton()) 288 .withCallback(button -> setLeftButton(button)) 289 .build(); 290 final IntentFilter filter = new IntentFilter(); 291 filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); 292 getContext().registerReceiverAsUser(mDevicePolicyReceiver, 293 UserHandle.ALL, filter, null, null); 294 KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback); 295 } 296 297 @Override onDetachedFromWindow()298 protected void onDetachedFromWindow() { 299 super.onDetachedFromWindow(); 300 mAccessibilityController.removeStateChangedCallback(this); 301 mRightExtension.destroy(); 302 mLeftExtension.destroy(); 303 getContext().unregisterReceiver(mDevicePolicyReceiver); 304 KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallback); 305 } 306 initAccessibility()307 private void initAccessibility() { 308 mLockIcon.setAccessibilityDelegate(mAccessibilityDelegate); 309 mLeftAffordanceView.setAccessibilityDelegate(mAccessibilityDelegate); 310 mRightAffordanceView.setAccessibilityDelegate(mAccessibilityDelegate); 311 } 312 313 @Override onConfigurationChanged(Configuration newConfig)314 protected void onConfigurationChanged(Configuration newConfig) { 315 super.onConfigurationChanged(newConfig); 316 mIndicationBottomMargin = getResources().getDimensionPixelSize( 317 R.dimen.keyguard_indication_margin_bottom); 318 mIndicationBottomMarginAmbient = getResources().getDimensionPixelSize( 319 R.dimen.keyguard_indication_margin_bottom_ambient); 320 MarginLayoutParams mlp = (MarginLayoutParams) mIndicationArea.getLayoutParams(); 321 if (mlp.bottomMargin != mIndicationBottomMargin) { 322 mlp.bottomMargin = mIndicationBottomMargin; 323 mIndicationArea.setLayoutParams(mlp); 324 } 325 326 // Respect font size setting. 327 mEnterpriseDisclosure.setTextSize(TypedValue.COMPLEX_UNIT_PX, 328 getResources().getDimensionPixelSize( 329 com.android.internal.R.dimen.text_size_small_material)); 330 mIndicationText.setTextSize(TypedValue.COMPLEX_UNIT_PX, 331 getResources().getDimensionPixelSize( 332 com.android.internal.R.dimen.text_size_small_material)); 333 334 ViewGroup.LayoutParams lp = mRightAffordanceView.getLayoutParams(); 335 lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_width); 336 lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height); 337 mRightAffordanceView.setLayoutParams(lp); 338 updateRightAffordanceIcon(); 339 340 lp = mLockIcon.getLayoutParams(); 341 lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_width); 342 lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height); 343 mLockIcon.setLayoutParams(lp); 344 mLockIcon.setContentDescription(getContext().getText(R.string.accessibility_unlock_button)); 345 mLockIcon.update(true /* force */); 346 347 lp = mLeftAffordanceView.getLayoutParams(); 348 lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_width); 349 lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height); 350 mLeftAffordanceView.setLayoutParams(lp); 351 updateLeftAffordanceIcon(); 352 } 353 updateRightAffordanceIcon()354 private void updateRightAffordanceIcon() { 355 IconState state = mRightButton.getIcon(); 356 mRightAffordanceView.setVisibility(!mDozing && state.isVisible ? View.VISIBLE : View.GONE); 357 mRightAffordanceView.setImageDrawable(state.drawable, state.tint); 358 mRightAffordanceView.setContentDescription(state.contentDescription); 359 } 360 setStatusBar(StatusBar statusBar)361 public void setStatusBar(StatusBar statusBar) { 362 mStatusBar = statusBar; 363 updateCameraVisibility(); // in case onFinishInflate() was called too early 364 } 365 setAffordanceHelper(KeyguardAffordanceHelper affordanceHelper)366 public void setAffordanceHelper(KeyguardAffordanceHelper affordanceHelper) { 367 mAffordanceHelper = affordanceHelper; 368 } 369 setUserSetupComplete(boolean userSetupComplete)370 public void setUserSetupComplete(boolean userSetupComplete) { 371 mUserSetupComplete = userSetupComplete; 372 updateCameraVisibility(); 373 updateLeftAffordanceIcon(); 374 } 375 getCameraIntent()376 private Intent getCameraIntent() { 377 return mRightButton.getIntent(); 378 } 379 380 /** 381 * Resolves the intent to launch the camera application. 382 */ resolveCameraIntent()383 public ResolveInfo resolveCameraIntent() { 384 return mContext.getPackageManager().resolveActivityAsUser(getCameraIntent(), 385 PackageManager.MATCH_DEFAULT_ONLY, 386 KeyguardUpdateMonitor.getCurrentUser()); 387 } 388 updateCameraVisibility()389 private void updateCameraVisibility() { 390 if (mRightAffordanceView == null) { 391 // Things are not set up yet; reply hazy, ask again later 392 return; 393 } 394 mRightAffordanceView.setVisibility(!mDozing && mRightButton.getIcon().isVisible 395 ? View.VISIBLE : View.GONE); 396 } 397 398 /** 399 * Set an alternate icon for the left assist affordance (replace the mic icon) 400 */ setLeftAssistIcon(Drawable drawable)401 public void setLeftAssistIcon(Drawable drawable) { 402 mLeftAssistIcon = drawable; 403 updateLeftAffordanceIcon(); 404 } 405 updateLeftAffordanceIcon()406 private void updateLeftAffordanceIcon() { 407 IconState state = mLeftButton.getIcon(); 408 mLeftAffordanceView.setVisibility(!mDozing && state.isVisible ? View.VISIBLE : View.GONE); 409 mLeftAffordanceView.setImageDrawable(state.drawable, state.tint); 410 mLeftAffordanceView.setContentDescription(state.contentDescription); 411 } 412 isLeftVoiceAssist()413 public boolean isLeftVoiceAssist() { 414 return mLeftIsVoiceAssist; 415 } 416 isPhoneVisible()417 private boolean isPhoneVisible() { 418 PackageManager pm = mContext.getPackageManager(); 419 return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) 420 && pm.resolveActivity(PHONE_INTENT, 0) != null; 421 } 422 423 @Override onStateChanged(boolean accessibilityEnabled, boolean touchExplorationEnabled)424 public void onStateChanged(boolean accessibilityEnabled, boolean touchExplorationEnabled) { 425 mRightAffordanceView.setClickable(touchExplorationEnabled); 426 mLeftAffordanceView.setClickable(touchExplorationEnabled); 427 mRightAffordanceView.setFocusable(accessibilityEnabled); 428 mLeftAffordanceView.setFocusable(accessibilityEnabled); 429 mLockIcon.update(); 430 } 431 432 @Override onClick(View v)433 public void onClick(View v) { 434 if (v == mRightAffordanceView) { 435 launchCamera(CAMERA_LAUNCH_SOURCE_AFFORDANCE); 436 } else if (v == mLeftAffordanceView) { 437 launchLeftAffordance(); 438 } if (v == mLockIcon) { 439 if (!mAccessibilityController.isAccessibilityEnabled()) { 440 handleTrustCircleClick(); 441 } else { 442 mStatusBar.animateCollapsePanels( 443 CommandQueue.FLAG_EXCLUDE_NONE, true /* force */); 444 } 445 } 446 } 447 448 @Override onLongClick(View v)449 public boolean onLongClick(View v) { 450 handleTrustCircleClick(); 451 return true; 452 } 453 handleTrustCircleClick()454 private void handleTrustCircleClick() { 455 mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_LOCK, 0 /* lengthDp - N/A */, 456 0 /* velocityDp - N/A */); 457 mIndicationController.showTransientIndication( 458 R.string.keyguard_indication_trust_disabled); 459 mLockPatternUtils.requireCredentialEntry(KeyguardUpdateMonitor.getCurrentUser()); 460 } 461 bindCameraPrewarmService()462 public void bindCameraPrewarmService() { 463 Intent intent = getCameraIntent(); 464 ActivityInfo targetInfo = PreviewInflater.getTargetActivityInfo(mContext, intent, 465 KeyguardUpdateMonitor.getCurrentUser(), true /* onlyDirectBootAware */); 466 if (targetInfo != null && targetInfo.metaData != null) { 467 String clazz = targetInfo.metaData.getString( 468 MediaStore.META_DATA_STILL_IMAGE_CAMERA_PREWARM_SERVICE); 469 if (clazz != null) { 470 Intent serviceIntent = new Intent(); 471 serviceIntent.setClassName(targetInfo.packageName, clazz); 472 serviceIntent.setAction(CameraPrewarmService.ACTION_PREWARM); 473 try { 474 if (getContext().bindServiceAsUser(serviceIntent, mPrewarmConnection, 475 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, 476 new UserHandle(UserHandle.USER_CURRENT))) { 477 mPrewarmBound = true; 478 } 479 } catch (SecurityException e) { 480 Log.w(TAG, "Unable to bind to prewarm service package=" + targetInfo.packageName 481 + " class=" + clazz, e); 482 } 483 } 484 } 485 } 486 unbindCameraPrewarmService(boolean launched)487 public void unbindCameraPrewarmService(boolean launched) { 488 if (mPrewarmBound) { 489 if (mPrewarmMessenger != null && launched) { 490 try { 491 mPrewarmMessenger.send(Message.obtain(null /* handler */, 492 CameraPrewarmService.MSG_CAMERA_FIRED)); 493 } catch (RemoteException e) { 494 Log.w(TAG, "Error sending camera fired message", e); 495 } 496 } 497 mContext.unbindService(mPrewarmConnection); 498 mPrewarmBound = false; 499 } 500 } 501 launchCamera(String source)502 public void launchCamera(String source) { 503 final Intent intent = getCameraIntent(); 504 intent.putExtra(EXTRA_CAMERA_LAUNCH_SOURCE, source); 505 boolean wouldLaunchResolverActivity = PreviewInflater.wouldLaunchResolverActivity( 506 mContext, intent, KeyguardUpdateMonitor.getCurrentUser()); 507 if (intent == SECURE_CAMERA_INTENT && !wouldLaunchResolverActivity) { 508 AsyncTask.execute(new Runnable() { 509 @Override 510 public void run() { 511 int result = ActivityManager.START_CANCELED; 512 513 // Normally an activity will set it's requested rotation 514 // animation on its window. However when launching an activity 515 // causes the orientation to change this is too late. In these cases 516 // the default animation is used. This doesn't look good for 517 // the camera (as it rotates the camera contents out of sync 518 // with physical reality). So, we ask the WindowManager to 519 // force the crossfade animation if an orientation change 520 // happens to occur during the launch. 521 ActivityOptions o = ActivityOptions.makeBasic(); 522 o.setDisallowEnterPictureInPictureWhileLaunching(true); 523 o.setRotationAnimationHint( 524 WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS); 525 try { 526 result = ActivityManager.getService().startActivityAsUser( 527 null, getContext().getBasePackageName(), 528 intent, 529 intent.resolveTypeIfNeeded(getContext().getContentResolver()), 530 null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, o.toBundle(), 531 UserHandle.CURRENT.getIdentifier()); 532 } catch (RemoteException e) { 533 Log.w(TAG, "Unable to start camera activity", e); 534 } 535 final boolean launched = isSuccessfulLaunch(result); 536 post(new Runnable() { 537 @Override 538 public void run() { 539 unbindCameraPrewarmService(launched); 540 } 541 }); 542 } 543 }); 544 } else { 545 546 // We need to delay starting the activity because ResolverActivity finishes itself if 547 // launched behind lockscreen. 548 mActivityStarter.startActivity(intent, false /* dismissShade */, 549 new ActivityStarter.Callback() { 550 @Override 551 public void onActivityStarted(int resultCode) { 552 unbindCameraPrewarmService(isSuccessfulLaunch(resultCode)); 553 } 554 }); 555 } 556 } 557 setDarkAmount(float darkAmount)558 public void setDarkAmount(float darkAmount) { 559 if (darkAmount == mDarkAmount) { 560 return; 561 } 562 mDarkAmount = darkAmount; 563 // Let's randomize the bottom margin every time we wake up to avoid burn-in. 564 if (darkAmount == 0) { 565 mIndicationBottomMarginAmbient = getResources().getDimensionPixelSize( 566 R.dimen.keyguard_indication_margin_bottom_ambient) 567 + (int) (Math.random() * mIndicationText.getTextSize()); 568 } 569 mIndicationArea.setAlpha(MathUtils.lerp(1f, 0.7f, darkAmount)); 570 mIndicationArea.setTranslationY(MathUtils.lerp(0, 571 mIndicationBottomMargin - mIndicationBottomMarginAmbient, darkAmount)); 572 } 573 isSuccessfulLaunch(int result)574 private static boolean isSuccessfulLaunch(int result) { 575 return result == ActivityManager.START_SUCCESS 576 || result == ActivityManager.START_DELIVERED_TO_TOP 577 || result == ActivityManager.START_TASK_TO_FRONT; 578 } 579 launchLeftAffordance()580 public void launchLeftAffordance() { 581 if (mLeftIsVoiceAssist) { 582 launchVoiceAssist(); 583 } else { 584 launchPhone(); 585 } 586 } 587 launchVoiceAssist()588 private void launchVoiceAssist() { 589 Runnable runnable = new Runnable() { 590 @Override 591 public void run() { 592 mAssistManager.launchVoiceAssistFromKeyguard(); 593 } 594 }; 595 if (mStatusBar.isKeyguardCurrentlySecure()) { 596 AsyncTask.execute(runnable); 597 } else { 598 boolean dismissShade = !TextUtils.isEmpty(mRightButtonStr) 599 && Dependency.get(TunerService.class).getValue(LOCKSCREEN_RIGHT_UNLOCK, 1) != 0; 600 mStatusBar.executeRunnableDismissingKeyguard(runnable, null /* cancelAction */, 601 dismissShade, false /* afterKeyguardGone */, true /* deferred */); 602 } 603 } 604 canLaunchVoiceAssist()605 private boolean canLaunchVoiceAssist() { 606 return mAssistManager.canVoiceAssistBeLaunchedFromKeyguard(); 607 } 608 launchPhone()609 private void launchPhone() { 610 final TelecomManager tm = TelecomManager.from(mContext); 611 if (tm.isInCall()) { 612 AsyncTask.execute(new Runnable() { 613 @Override 614 public void run() { 615 tm.showInCallScreen(false /* showDialpad */); 616 } 617 }); 618 } else { 619 boolean dismissShade = !TextUtils.isEmpty(mLeftButtonStr) 620 && Dependency.get(TunerService.class).getValue(LOCKSCREEN_LEFT_UNLOCK, 1) != 0; 621 mActivityStarter.startActivity(mLeftButton.getIntent(), dismissShade); 622 } 623 } 624 625 626 @Override onVisibilityChanged(View changedView, int visibility)627 protected void onVisibilityChanged(View changedView, int visibility) { 628 super.onVisibilityChanged(changedView, visibility); 629 if (changedView == this && visibility == VISIBLE) { 630 mLockIcon.update(); 631 updateCameraVisibility(); 632 } 633 } 634 getLeftView()635 public KeyguardAffordanceView getLeftView() { 636 return mLeftAffordanceView; 637 } 638 getRightView()639 public KeyguardAffordanceView getRightView() { 640 return mRightAffordanceView; 641 } 642 getLeftPreview()643 public View getLeftPreview() { 644 return mLeftPreview; 645 } 646 getRightPreview()647 public View getRightPreview() { 648 return mCameraPreview; 649 } 650 getLockIcon()651 public LockIcon getLockIcon() { 652 return mLockIcon; 653 } 654 getIndicationArea()655 public View getIndicationArea() { 656 return mIndicationArea; 657 } 658 659 @Override hasOverlappingRendering()660 public boolean hasOverlappingRendering() { 661 return false; 662 } 663 664 @Override onUnlockMethodStateChanged()665 public void onUnlockMethodStateChanged() { 666 mLockIcon.update(); 667 updateCameraVisibility(); 668 } 669 inflateCameraPreview()670 private void inflateCameraPreview() { 671 View previewBefore = mCameraPreview; 672 boolean visibleBefore = false; 673 if (previewBefore != null) { 674 mPreviewContainer.removeView(previewBefore); 675 visibleBefore = previewBefore.getVisibility() == View.VISIBLE; 676 } 677 mCameraPreview = mPreviewInflater.inflatePreview(getCameraIntent()); 678 if (mCameraPreview != null) { 679 mPreviewContainer.addView(mCameraPreview); 680 mCameraPreview.setVisibility(visibleBefore ? View.VISIBLE : View.INVISIBLE); 681 } 682 if (mAffordanceHelper != null) { 683 mAffordanceHelper.updatePreviews(); 684 } 685 } 686 updateLeftPreview()687 private void updateLeftPreview() { 688 View previewBefore = mLeftPreview; 689 if (previewBefore != null) { 690 mPreviewContainer.removeView(previewBefore); 691 } 692 if (mLeftIsVoiceAssist) { 693 mLeftPreview = mPreviewInflater.inflatePreviewFromService( 694 mAssistManager.getVoiceInteractorComponentName()); 695 } else { 696 mLeftPreview = mPreviewInflater.inflatePreview(mLeftButton.getIntent()); 697 } 698 if (mLeftPreview != null) { 699 mPreviewContainer.addView(mLeftPreview); 700 mLeftPreview.setVisibility(View.INVISIBLE); 701 } 702 if (mAffordanceHelper != null) { 703 mAffordanceHelper.updatePreviews(); 704 } 705 } 706 startFinishDozeAnimation()707 public void startFinishDozeAnimation() { 708 long delay = 0; 709 if (mLeftAffordanceView.getVisibility() == View.VISIBLE) { 710 startFinishDozeAnimationElement(mLeftAffordanceView, delay); 711 delay += DOZE_ANIMATION_STAGGER_DELAY; 712 } 713 startFinishDozeAnimationElement(mLockIcon, delay); 714 delay += DOZE_ANIMATION_STAGGER_DELAY; 715 if (mRightAffordanceView.getVisibility() == View.VISIBLE) { 716 startFinishDozeAnimationElement(mRightAffordanceView, delay); 717 } 718 } 719 startFinishDozeAnimationElement(View element, long delay)720 private void startFinishDozeAnimationElement(View element, long delay) { 721 element.setAlpha(0f); 722 element.setTranslationY(element.getHeight() / 2); 723 element.animate() 724 .alpha(1f) 725 .translationY(0f) 726 .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN) 727 .setStartDelay(delay) 728 .setDuration(DOZE_ANIMATION_ELEMENT_DURATION); 729 } 730 731 private final BroadcastReceiver mDevicePolicyReceiver = new BroadcastReceiver() { 732 @Override 733 public void onReceive(Context context, Intent intent) { 734 post(new Runnable() { 735 @Override 736 public void run() { 737 updateCameraVisibility(); 738 } 739 }); 740 } 741 }; 742 743 private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback = 744 new KeyguardUpdateMonitorCallback() { 745 @Override 746 public void onUserSwitchComplete(int userId) { 747 updateCameraVisibility(); 748 } 749 750 @Override 751 public void onStartedWakingUp() { 752 mLockIcon.setDeviceInteractive(true); 753 } 754 755 @Override 756 public void onFinishedGoingToSleep(int why) { 757 mLockIcon.setDeviceInteractive(false); 758 } 759 760 @Override 761 public void onScreenTurnedOn() { 762 mLockIcon.setScreenOn(true); 763 } 764 765 @Override 766 public void onScreenTurnedOff() { 767 mLockIcon.setScreenOn(false); 768 } 769 770 @Override 771 public void onKeyguardVisibilityChanged(boolean showing) { 772 mLockIcon.update(); 773 } 774 775 @Override 776 public void onFingerprintRunningStateChanged(boolean running) { 777 mLockIcon.update(); 778 } 779 780 @Override 781 public void onStrongAuthStateChanged(int userId) { 782 mLockIcon.update(); 783 } 784 785 @Override 786 public void onUserUnlocked() { 787 inflateCameraPreview(); 788 updateCameraVisibility(); 789 updateLeftAffordance(); 790 } 791 }; 792 setKeyguardIndicationController( KeyguardIndicationController keyguardIndicationController)793 public void setKeyguardIndicationController( 794 KeyguardIndicationController keyguardIndicationController) { 795 mIndicationController = keyguardIndicationController; 796 } 797 updateLeftAffordance()798 public void updateLeftAffordance() { 799 updateLeftAffordanceIcon(); 800 updateLeftPreview(); 801 } 802 onKeyguardShowingChanged()803 public void onKeyguardShowingChanged() { 804 updateLeftAffordance(); 805 inflateCameraPreview(); 806 } 807 setRightButton(IntentButton button)808 private void setRightButton(IntentButton button) { 809 mRightButton = button; 810 updateRightAffordanceIcon(); 811 updateCameraVisibility(); 812 inflateCameraPreview(); 813 } 814 setLeftButton(IntentButton button)815 private void setLeftButton(IntentButton button) { 816 mLeftButton = button; 817 if (!(mLeftButton instanceof DefaultLeftButton)) { 818 mLeftIsVoiceAssist = false; 819 } 820 updateLeftAffordance(); 821 } 822 setDozing(boolean dozing, boolean animate)823 public void setDozing(boolean dozing, boolean animate) { 824 mDozing = dozing; 825 826 updateCameraVisibility(); 827 updateLeftAffordanceIcon(); 828 829 if (dozing) { 830 mLockIcon.setVisibility(INVISIBLE); 831 mOverlayContainer.setVisibility(INVISIBLE); 832 } else { 833 mLockIcon.setVisibility(VISIBLE); 834 mOverlayContainer.setVisibility(VISIBLE); 835 if (animate) { 836 startFinishDozeAnimation(); 837 } 838 } 839 } 840 dozeTimeTick()841 public void dozeTimeTick() { 842 if (mDarkAmount == 1) { 843 // Move indication every minute to avoid burn-in 844 final int dozeTranslation = mIndicationBottomMargin - mIndicationBottomMarginAmbient; 845 mIndicationArea.setTranslationY(dozeTranslation + (float) Math.random() * 5); 846 } 847 } 848 setBurnInXOffset(int burnInXOffset)849 public void setBurnInXOffset(int burnInXOffset) { 850 if (mBurnInXOffset == burnInXOffset) { 851 return; 852 } 853 mBurnInXOffset = burnInXOffset; 854 mIndicationArea.setTranslationX(burnInXOffset); 855 } 856 857 private class DefaultLeftButton implements IntentButton { 858 859 private IconState mIconState = new IconState(); 860 861 @Override getIcon()862 public IconState getIcon() { 863 mLeftIsVoiceAssist = canLaunchVoiceAssist(); 864 final boolean showAffordance = 865 getResources().getBoolean(R.bool.config_keyguardShowLeftAffordance); 866 if (mLeftIsVoiceAssist) { 867 mIconState.isVisible = mUserSetupComplete && showAffordance; 868 if (mLeftAssistIcon == null) { 869 mIconState.drawable = mContext.getDrawable(R.drawable.ic_mic_26dp); 870 } else { 871 mIconState.drawable = mLeftAssistIcon; 872 } 873 mIconState.contentDescription = mContext.getString( 874 R.string.accessibility_voice_assist_button); 875 } else { 876 mIconState.isVisible = mUserSetupComplete && showAffordance && isPhoneVisible(); 877 mIconState.drawable = mContext.getDrawable(R.drawable.ic_phone_24dp); 878 mIconState.contentDescription = mContext.getString( 879 R.string.accessibility_phone_button); 880 } 881 return mIconState; 882 } 883 884 @Override getIntent()885 public Intent getIntent() { 886 return PHONE_INTENT; 887 } 888 } 889 890 private class DefaultRightButton implements IntentButton { 891 892 private IconState mIconState = new IconState(); 893 894 @Override getIcon()895 public IconState getIcon() { 896 ResolveInfo resolved = resolveCameraIntent(); 897 boolean isCameraDisabled = (mStatusBar != null) && !mStatusBar.isCameraAllowedByAdmin(); 898 mIconState.isVisible = !isCameraDisabled && resolved != null 899 && getResources().getBoolean(R.bool.config_keyguardShowCameraAffordance) 900 && mUserSetupComplete; 901 mIconState.drawable = mContext.getDrawable(R.drawable.ic_camera_alt_24dp); 902 mIconState.contentDescription = 903 mContext.getString(R.string.accessibility_camera_button); 904 return mIconState; 905 } 906 907 @Override getIntent()908 public Intent getIntent() { 909 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); 910 boolean canSkipBouncer = updateMonitor.getUserCanSkipBouncer( 911 KeyguardUpdateMonitor.getCurrentUser()); 912 boolean secure = mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser()); 913 return (secure && !canSkipBouncer) ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT; 914 } 915 } 916 917 @Override onApplyWindowInsets(WindowInsets insets)918 public WindowInsets onApplyWindowInsets(WindowInsets insets) { 919 int bottom = insets.getDisplayCutout() != null 920 ? insets.getDisplayCutout().getSafeInsetBottom() : 0; 921 if (isPaddingRelative()) { 922 setPaddingRelative(getPaddingStart(), getPaddingTop(), getPaddingEnd(), bottom); 923 } else { 924 setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), bottom); 925 } 926 return insets; 927 } 928 } 929