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