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