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 android.app.ActivityManager;
20 import android.app.ActivityManagerNative;
21 import android.app.admin.DevicePolicyManager;
22 import android.content.BroadcastReceiver;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.content.ServiceConnection;
28 import android.content.pm.ActivityInfo;
29 import android.content.pm.PackageManager;
30 import android.content.pm.ResolveInfo;
31 import android.content.res.Configuration;
32 import android.hardware.fingerprint.FingerprintManager;
33 import android.os.AsyncTask;
34 import android.os.Bundle;
35 import android.os.IBinder;
36 import android.os.Message;
37 import android.os.Messenger;
38 import android.os.RemoteException;
39 import android.os.UserHandle;
40 import android.provider.MediaStore;
41 import android.service.media.CameraPrewarmService;
42 import android.telecom.TelecomManager;
43 import android.util.AttributeSet;
44 import android.util.Log;
45 import android.util.TypedValue;
46 import android.view.View;
47 import android.view.ViewGroup;
48 import android.view.accessibility.AccessibilityNodeInfo;
49 import android.view.animation.AnimationUtils;
50 import android.view.animation.Interpolator;
51 import android.widget.FrameLayout;
52 import android.widget.TextView;
53 
54 import com.android.internal.widget.LockPatternUtils;
55 import com.android.keyguard.KeyguardUpdateMonitor;
56 import com.android.keyguard.KeyguardUpdateMonitorCallback;
57 import com.android.systemui.EventLogConstants;
58 import com.android.systemui.EventLogTags;
59 import com.android.systemui.R;
60 import com.android.systemui.assist.AssistManager;
61 import com.android.systemui.statusbar.CommandQueue;
62 import com.android.systemui.statusbar.KeyguardAffordanceView;
63 import com.android.systemui.statusbar.KeyguardIndicationController;
64 import com.android.systemui.statusbar.policy.AccessibilityController;
65 import com.android.systemui.statusbar.policy.FlashlightController;
66 import com.android.systemui.statusbar.policy.PreviewInflater;
67 
68 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
69 import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
70 
71 /**
72  * Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
73  * text.
74  */
75 public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickListener,
76         UnlockMethodCache.OnUnlockMethodChangedListener,
77         AccessibilityController.AccessibilityStateChangedCallback, View.OnLongClickListener {
78 
79     final static String TAG = "PhoneStatusBar/KeyguardBottomAreaView";
80 
81     private static final Intent SECURE_CAMERA_INTENT =
82             new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
83                     .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
84     private static final Intent INSECURE_CAMERA_INTENT =
85             new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
86     private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL);
87     private static final int DOZE_ANIMATION_STAGGER_DELAY = 48;
88     private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250;
89     private static final long TRANSIENT_FP_ERROR_TIMEOUT = 1300;
90 
91     private KeyguardAffordanceView mCameraImageView;
92     private KeyguardAffordanceView mLeftAffordanceView;
93     private LockIcon mLockIcon;
94     private TextView mIndicationText;
95     private ViewGroup mPreviewContainer;
96 
97     private View mLeftPreview;
98     private View mCameraPreview;
99 
100     private ActivityStarter mActivityStarter;
101     private UnlockMethodCache mUnlockMethodCache;
102     private LockPatternUtils mLockPatternUtils;
103     private FlashlightController mFlashlightController;
104     private PreviewInflater mPreviewInflater;
105     private KeyguardIndicationController mIndicationController;
106     private AccessibilityController mAccessibilityController;
107     private PhoneStatusBar mPhoneStatusBar;
108 
109     private final Interpolator mLinearOutSlowInInterpolator;
110     private boolean mUserSetupComplete;
111     private boolean mPrewarmBound;
112     private Messenger mPrewarmMessenger;
113     private final ServiceConnection mPrewarmConnection = new ServiceConnection() {
114 
115         @Override
116         public void onServiceConnected(ComponentName name, IBinder service) {
117             mPrewarmMessenger = new Messenger(service);
118         }
119 
120         @Override
121         public void onServiceDisconnected(ComponentName name) {
122             mPrewarmMessenger = null;
123         }
124     };
125 
126     private boolean mLeftIsVoiceAssist;
127     private AssistManager mAssistManager;
128 
KeyguardBottomAreaView(Context context)129     public KeyguardBottomAreaView(Context context) {
130         this(context, null);
131     }
132 
KeyguardBottomAreaView(Context context, AttributeSet attrs)133     public KeyguardBottomAreaView(Context context, AttributeSet attrs) {
134         this(context, attrs, 0);
135     }
136 
KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr)137     public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr) {
138         this(context, attrs, defStyleAttr, 0);
139     }
140 
KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)141     public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr,
142             int defStyleRes) {
143         super(context, attrs, defStyleAttr, defStyleRes);
144         mLinearOutSlowInInterpolator =
145                 AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
146     }
147 
148     private AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() {
149         @Override
150         public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
151             super.onInitializeAccessibilityNodeInfo(host, info);
152             String label = null;
153             if (host == mLockIcon) {
154                 label = getResources().getString(R.string.unlock_label);
155             } else if (host == mCameraImageView) {
156                 label = getResources().getString(R.string.camera_label);
157             } else if (host == mLeftAffordanceView) {
158                 if (mLeftIsVoiceAssist) {
159                     label = getResources().getString(R.string.voice_assist_label);
160                 } else {
161                     label = getResources().getString(R.string.phone_label);
162                 }
163             }
164             info.addAction(new AccessibilityAction(ACTION_CLICK, label));
165         }
166 
167         @Override
168         public boolean performAccessibilityAction(View host, int action, Bundle args) {
169             if (action == ACTION_CLICK) {
170                 if (host == mLockIcon) {
171                     mPhoneStatusBar.animateCollapsePanels(
172                             CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
173                     return true;
174                 } else if (host == mCameraImageView) {
175                     launchCamera();
176                     return true;
177                 } else if (host == mLeftAffordanceView) {
178                     launchLeftAffordance();
179                     return true;
180                 }
181             }
182             return super.performAccessibilityAction(host, action, args);
183         }
184     };
185 
186     @Override
onFinishInflate()187     protected void onFinishInflate() {
188         super.onFinishInflate();
189         mLockPatternUtils = new LockPatternUtils(mContext);
190         mPreviewContainer = (ViewGroup) findViewById(R.id.preview_container);
191         mCameraImageView = (KeyguardAffordanceView) findViewById(R.id.camera_button);
192         mLeftAffordanceView = (KeyguardAffordanceView) findViewById(R.id.left_button);
193         mLockIcon = (LockIcon) findViewById(R.id.lock_icon);
194         mIndicationText = (TextView) findViewById(R.id.keyguard_indication_text);
195         watchForCameraPolicyChanges();
196         updateCameraVisibility();
197         mUnlockMethodCache = UnlockMethodCache.getInstance(getContext());
198         mUnlockMethodCache.addListener(this);
199         mLockIcon.update();
200         setClipChildren(false);
201         setClipToPadding(false);
202         mPreviewInflater = new PreviewInflater(mContext, new LockPatternUtils(mContext));
203         inflateCameraPreview();
204         mLockIcon.setOnClickListener(this);
205         mLockIcon.setOnLongClickListener(this);
206         mCameraImageView.setOnClickListener(this);
207         mLeftAffordanceView.setOnClickListener(this);
208         initAccessibility();
209     }
210 
initAccessibility()211     private void initAccessibility() {
212         mLockIcon.setAccessibilityDelegate(mAccessibilityDelegate);
213         mLeftAffordanceView.setAccessibilityDelegate(mAccessibilityDelegate);
214         mCameraImageView.setAccessibilityDelegate(mAccessibilityDelegate);
215     }
216 
217     @Override
onConfigurationChanged(Configuration newConfig)218     protected void onConfigurationChanged(Configuration newConfig) {
219         super.onConfigurationChanged(newConfig);
220         int indicationBottomMargin = getResources().getDimensionPixelSize(
221                 R.dimen.keyguard_indication_margin_bottom);
222         MarginLayoutParams mlp = (MarginLayoutParams) mIndicationText.getLayoutParams();
223         if (mlp.bottomMargin != indicationBottomMargin) {
224             mlp.bottomMargin = indicationBottomMargin;
225             mIndicationText.setLayoutParams(mlp);
226         }
227 
228         // Respect font size setting.
229         mIndicationText.setTextSize(TypedValue.COMPLEX_UNIT_PX,
230                 getResources().getDimensionPixelSize(
231                         com.android.internal.R.dimen.text_size_small_material));
232     }
233 
setActivityStarter(ActivityStarter activityStarter)234     public void setActivityStarter(ActivityStarter activityStarter) {
235         mActivityStarter = activityStarter;
236     }
237 
setFlashlightController(FlashlightController flashlightController)238     public void setFlashlightController(FlashlightController flashlightController) {
239         mFlashlightController = flashlightController;
240     }
241 
setAccessibilityController(AccessibilityController accessibilityController)242     public void setAccessibilityController(AccessibilityController accessibilityController) {
243         mAccessibilityController = accessibilityController;
244         mLockIcon.setAccessibilityController(accessibilityController);
245         accessibilityController.addStateChangedCallback(this);
246     }
247 
setPhoneStatusBar(PhoneStatusBar phoneStatusBar)248     public void setPhoneStatusBar(PhoneStatusBar phoneStatusBar) {
249         mPhoneStatusBar = phoneStatusBar;
250         updateCameraVisibility(); // in case onFinishInflate() was called too early
251     }
252 
setUserSetupComplete(boolean userSetupComplete)253     public void setUserSetupComplete(boolean userSetupComplete) {
254         mUserSetupComplete = userSetupComplete;
255         updateCameraVisibility();
256         updateLeftAffordanceIcon();
257     }
258 
getCameraIntent()259     private Intent getCameraIntent() {
260         KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
261         boolean canSkipBouncer = updateMonitor.getUserCanSkipBouncer(
262                 KeyguardUpdateMonitor.getCurrentUser());
263         boolean secure = mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser());
264         return (secure && !canSkipBouncer) ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT;
265     }
266 
updateCameraVisibility()267     private void updateCameraVisibility() {
268         if (mCameraImageView == null) {
269             // Things are not set up yet; reply hazy, ask again later
270             return;
271         }
272         ResolveInfo resolved = mContext.getPackageManager().resolveActivityAsUser(getCameraIntent(),
273                 PackageManager.MATCH_DEFAULT_ONLY,
274                 KeyguardUpdateMonitor.getCurrentUser());
275         boolean visible = !isCameraDisabledByDpm() && resolved != null
276                 && getResources().getBoolean(R.bool.config_keyguardShowCameraAffordance)
277                 && mUserSetupComplete;
278         mCameraImageView.setVisibility(visible ? View.VISIBLE : View.GONE);
279     }
280 
updateLeftAffordanceIcon()281     private void updateLeftAffordanceIcon() {
282         mLeftIsVoiceAssist = canLaunchVoiceAssist();
283         int drawableId;
284         int contentDescription;
285         boolean visible = mUserSetupComplete;
286         if (mLeftIsVoiceAssist) {
287             drawableId = R.drawable.ic_mic_26dp;
288             contentDescription = R.string.accessibility_voice_assist_button;
289         } else {
290             visible &= isPhoneVisible();
291             drawableId = R.drawable.ic_phone_24dp;
292             contentDescription = R.string.accessibility_phone_button;
293         }
294         mLeftAffordanceView.setVisibility(visible ? View.VISIBLE : View.GONE);
295         mLeftAffordanceView.setImageDrawable(mContext.getDrawable(drawableId));
296         mLeftAffordanceView.setContentDescription(mContext.getString(contentDescription));
297     }
298 
isLeftVoiceAssist()299     public boolean isLeftVoiceAssist() {
300         return mLeftIsVoiceAssist;
301     }
302 
isPhoneVisible()303     private boolean isPhoneVisible() {
304         PackageManager pm = mContext.getPackageManager();
305         return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
306                 && pm.resolveActivity(PHONE_INTENT, 0) != null;
307     }
308 
isCameraDisabledByDpm()309     private boolean isCameraDisabledByDpm() {
310         final DevicePolicyManager dpm =
311                 (DevicePolicyManager) getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
312         if (dpm != null && mPhoneStatusBar != null) {
313             try {
314                 final int userId = ActivityManagerNative.getDefault().getCurrentUser().id;
315                 final int disabledFlags = dpm.getKeyguardDisabledFeatures(null, userId);
316                 final  boolean disabledBecauseKeyguardSecure =
317                         (disabledFlags & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0
318                                 && mPhoneStatusBar.isKeyguardSecure();
319                 return dpm.getCameraDisabled(null) || disabledBecauseKeyguardSecure;
320             } catch (RemoteException e) {
321                 Log.e(TAG, "Can't get userId", e);
322             }
323         }
324         return false;
325     }
326 
watchForCameraPolicyChanges()327     private void watchForCameraPolicyChanges() {
328         final IntentFilter filter = new IntentFilter();
329         filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
330         getContext().registerReceiverAsUser(mDevicePolicyReceiver,
331                 UserHandle.ALL, filter, null, null);
332         KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback);
333     }
334 
335     @Override
onStateChanged(boolean accessibilityEnabled, boolean touchExplorationEnabled)336     public void onStateChanged(boolean accessibilityEnabled, boolean touchExplorationEnabled) {
337         mCameraImageView.setClickable(touchExplorationEnabled);
338         mLeftAffordanceView.setClickable(touchExplorationEnabled);
339         mCameraImageView.setFocusable(accessibilityEnabled);
340         mLeftAffordanceView.setFocusable(accessibilityEnabled);
341         mLockIcon.update();
342     }
343 
344     @Override
onClick(View v)345     public void onClick(View v) {
346         if (v == mCameraImageView) {
347             launchCamera();
348         } else if (v == mLeftAffordanceView) {
349             launchLeftAffordance();
350         } if (v == mLockIcon) {
351             if (!mAccessibilityController.isAccessibilityEnabled()) {
352                 handleTrustCircleClick();
353             } else {
354                 mPhoneStatusBar.animateCollapsePanels(
355                         CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
356             }
357         }
358     }
359 
360     @Override
onLongClick(View v)361     public boolean onLongClick(View v) {
362         handleTrustCircleClick();
363         return true;
364     }
365 
handleTrustCircleClick()366     private void handleTrustCircleClick() {
367         EventLogTags.writeSysuiLockscreenGesture(
368                 EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_TAP_LOCK, 0 /* lengthDp - N/A */,
369                 0 /* velocityDp - N/A */);
370         mIndicationController.showTransientIndication(
371                 R.string.keyguard_indication_trust_disabled);
372         mLockPatternUtils.requireCredentialEntry(KeyguardUpdateMonitor.getCurrentUser());
373     }
374 
bindCameraPrewarmService()375     public void bindCameraPrewarmService() {
376         Intent intent = getCameraIntent();
377         ActivityInfo targetInfo = PreviewInflater.getTargetActivityInfo(mContext, intent,
378                 KeyguardUpdateMonitor.getCurrentUser());
379         if (targetInfo != null && targetInfo.metaData != null) {
380             String clazz = targetInfo.metaData.getString(
381                     MediaStore.META_DATA_STILL_IMAGE_CAMERA_PREWARM_SERVICE);
382             if (clazz != null) {
383                 Intent serviceIntent = new Intent();
384                 serviceIntent.setClassName(targetInfo.packageName, clazz);
385                 serviceIntent.setAction(CameraPrewarmService.ACTION_PREWARM);
386                 try {
387                     if (getContext().bindServiceAsUser(serviceIntent, mPrewarmConnection,
388                             Context.BIND_AUTO_CREATE, new UserHandle(UserHandle.USER_CURRENT))) {
389                         mPrewarmBound = true;
390                     }
391                 } catch (SecurityException e) {
392                     Log.w(TAG, "Unable to bind to prewarm service package=" + targetInfo.packageName
393                             + " class=" + clazz, e);
394                 }
395             }
396         }
397     }
398 
unbindCameraPrewarmService(boolean launched)399     public void unbindCameraPrewarmService(boolean launched) {
400         if (mPrewarmBound) {
401             if (mPrewarmMessenger != null && launched) {
402                 try {
403                     mPrewarmMessenger.send(Message.obtain(null /* handler */,
404                             CameraPrewarmService.MSG_CAMERA_FIRED));
405                 } catch (RemoteException e) {
406                     Log.w(TAG, "Error sending camera fired message", e);
407                 }
408             }
409             mContext.unbindService(mPrewarmConnection);
410             mPrewarmBound = false;
411         }
412     }
413 
launchCamera()414     public void launchCamera() {
415         final Intent intent = getCameraIntent();
416         boolean wouldLaunchResolverActivity = PreviewInflater.wouldLaunchResolverActivity(
417                 mContext, intent, KeyguardUpdateMonitor.getCurrentUser());
418         if (intent == SECURE_CAMERA_INTENT && !wouldLaunchResolverActivity) {
419             AsyncTask.execute(new Runnable() {
420                 @Override
421                 public void run() {
422                     int result = ActivityManager.START_CANCELED;
423                     try {
424                         result = ActivityManagerNative.getDefault().startActivityAsUser(
425                                 null, getContext().getBasePackageName(),
426                                 intent,
427                                 intent.resolveTypeIfNeeded(getContext().getContentResolver()),
428                                 null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, null,
429                                 UserHandle.CURRENT.getIdentifier());
430                     } catch (RemoteException e) {
431                         Log.w(TAG, "Unable to start camera activity", e);
432                     }
433                     mActivityStarter.preventNextAnimation();
434                     final boolean launched = isSuccessfulLaunch(result);
435                     post(new Runnable() {
436                         @Override
437                         public void run() {
438                             unbindCameraPrewarmService(launched);
439                         }
440                     });
441                 }
442             });
443         } else {
444 
445             // We need to delay starting the activity because ResolverActivity finishes itself if
446             // launched behind lockscreen.
447             mActivityStarter.startActivity(intent, false /* dismissShade */,
448                     new ActivityStarter.Callback() {
449                         @Override
450                         public void onActivityStarted(int resultCode) {
451                             unbindCameraPrewarmService(isSuccessfulLaunch(resultCode));
452                         }
453                     });
454         }
455     }
456 
isSuccessfulLaunch(int result)457     private static boolean isSuccessfulLaunch(int result) {
458         return result == ActivityManager.START_SUCCESS
459                 || result == ActivityManager.START_DELIVERED_TO_TOP
460                 || result == ActivityManager.START_TASK_TO_FRONT;
461     }
462 
launchLeftAffordance()463     public void launchLeftAffordance() {
464         if (mLeftIsVoiceAssist) {
465             launchVoiceAssist();
466         } else {
467             launchPhone();
468         }
469     }
470 
launchVoiceAssist()471     private void launchVoiceAssist() {
472         Runnable runnable = new Runnable() {
473             @Override
474             public void run() {
475                 mAssistManager.launchVoiceAssistFromKeyguard();
476                 mActivityStarter.preventNextAnimation();
477             }
478         };
479         if (mPhoneStatusBar.isKeyguardCurrentlySecure()) {
480             AsyncTask.execute(runnable);
481         } else {
482             mPhoneStatusBar.executeRunnableDismissingKeyguard(runnable, null /* cancelAction */,
483                     false /* dismissShade */, false /* afterKeyguardGone */);
484         }
485     }
486 
canLaunchVoiceAssist()487     private boolean canLaunchVoiceAssist() {
488         return mAssistManager.canVoiceAssistBeLaunchedFromKeyguard();
489     }
490 
launchPhone()491     private void launchPhone() {
492         final TelecomManager tm = TelecomManager.from(mContext);
493         if (tm.isInCall()) {
494             AsyncTask.execute(new Runnable() {
495                 @Override
496                 public void run() {
497                     tm.showInCallScreen(false /* showDialpad */);
498                 }
499             });
500         } else {
501             mActivityStarter.startActivity(PHONE_INTENT, false /* dismissShade */);
502         }
503     }
504 
505 
506     @Override
onVisibilityChanged(View changedView, int visibility)507     protected void onVisibilityChanged(View changedView, int visibility) {
508         super.onVisibilityChanged(changedView, visibility);
509         if (changedView == this && visibility == VISIBLE) {
510             mLockIcon.update();
511             updateCameraVisibility();
512         }
513     }
514 
getLeftView()515     public KeyguardAffordanceView getLeftView() {
516         return mLeftAffordanceView;
517     }
518 
getRightView()519     public KeyguardAffordanceView getRightView() {
520         return mCameraImageView;
521     }
522 
getLeftPreview()523     public View getLeftPreview() {
524         return mLeftPreview;
525     }
526 
getRightPreview()527     public View getRightPreview() {
528         return mCameraPreview;
529     }
530 
getLockIcon()531     public KeyguardAffordanceView getLockIcon() {
532         return mLockIcon;
533     }
534 
getIndicationView()535     public View getIndicationView() {
536         return mIndicationText;
537     }
538 
539     @Override
hasOverlappingRendering()540     public boolean hasOverlappingRendering() {
541         return false;
542     }
543 
544     @Override
onUnlockMethodStateChanged()545     public void onUnlockMethodStateChanged() {
546         mLockIcon.update();
547         updateCameraVisibility();
548     }
549 
inflateCameraPreview()550     private void inflateCameraPreview() {
551         mCameraPreview = mPreviewInflater.inflatePreview(getCameraIntent());
552         if (mCameraPreview != null) {
553             mPreviewContainer.addView(mCameraPreview);
554             mCameraPreview.setVisibility(View.INVISIBLE);
555         }
556     }
557 
updateLeftPreview()558     private void updateLeftPreview() {
559         View previewBefore = mLeftPreview;
560         if (previewBefore != null) {
561             mPreviewContainer.removeView(previewBefore);
562         }
563         if (mLeftIsVoiceAssist) {
564             mLeftPreview = mPreviewInflater.inflatePreviewFromService(
565                     mAssistManager.getVoiceInteractorComponentName());
566         } else {
567             mLeftPreview = mPreviewInflater.inflatePreview(PHONE_INTENT);
568         }
569         if (mLeftPreview != null) {
570             mPreviewContainer.addView(mLeftPreview);
571             mLeftPreview.setVisibility(View.INVISIBLE);
572         }
573     }
574 
startFinishDozeAnimation()575     public void startFinishDozeAnimation() {
576         long delay = 0;
577         if (mLeftAffordanceView.getVisibility() == View.VISIBLE) {
578             startFinishDozeAnimationElement(mLeftAffordanceView, delay);
579             delay += DOZE_ANIMATION_STAGGER_DELAY;
580         }
581         startFinishDozeAnimationElement(mLockIcon, delay);
582         delay += DOZE_ANIMATION_STAGGER_DELAY;
583         if (mCameraImageView.getVisibility() == View.VISIBLE) {
584             startFinishDozeAnimationElement(mCameraImageView, delay);
585         }
586         mIndicationText.setAlpha(0f);
587         mIndicationText.animate()
588                 .alpha(1f)
589                 .setInterpolator(mLinearOutSlowInInterpolator)
590                 .setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
591     }
592 
startFinishDozeAnimationElement(View element, long delay)593     private void startFinishDozeAnimationElement(View element, long delay) {
594         element.setAlpha(0f);
595         element.setTranslationY(element.getHeight() / 2);
596         element.animate()
597                 .alpha(1f)
598                 .translationY(0f)
599                 .setInterpolator(mLinearOutSlowInInterpolator)
600                 .setStartDelay(delay)
601                 .setDuration(DOZE_ANIMATION_ELEMENT_DURATION);
602     }
603 
604     private final BroadcastReceiver mDevicePolicyReceiver = new BroadcastReceiver() {
605         @Override
606         public void onReceive(Context context, Intent intent) {
607             post(new Runnable() {
608                 @Override
609                 public void run() {
610                     updateCameraVisibility();
611                 }
612             });
613         }
614     };
615 
616     private final Runnable mTransientFpErrorClearRunnable = new Runnable() {
617         @Override
618         public void run() {
619             mLockIcon.setTransientFpError(false);
620             mIndicationController.hideTransientIndication();
621         }
622     };
623 
624     private final Runnable mHideTransientIndicationRunnable = new Runnable() {
625         @Override
626         public void run() {
627             mIndicationController.hideTransientIndication();
628         }
629     };
630 
631     private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
632             new KeyguardUpdateMonitorCallback() {
633         @Override
634         public void onUserSwitchComplete(int userId) {
635             updateCameraVisibility();
636         }
637 
638         @Override
639         public void onStartedWakingUp() {
640             mLockIcon.setDeviceInteractive(true);
641         }
642 
643         @Override
644         public void onFinishedGoingToSleep(int why) {
645             mLockIcon.setDeviceInteractive(false);
646         }
647 
648         @Override
649         public void onKeyguardVisibilityChanged(boolean showing) {
650             mLockIcon.update();
651         }
652 
653         @Override
654         public void onFingerprintAuthenticated(int userId, boolean wakeAndUnlocking) {
655         }
656 
657         @Override
658         public void onFingerprintRunningStateChanged(boolean running) {
659             mLockIcon.update();
660         }
661 
662         @Override
663         public void onFingerprintHelp(int msgId, String helpString) {
664             if (!KeyguardUpdateMonitor.getInstance(mContext).isUnlockingWithFingerprintAllowed()) {
665                 return;
666             }
667             mLockIcon.setTransientFpError(true);
668             mIndicationController.showTransientIndication(helpString,
669                     getResources().getColor(R.color.system_warning_color, null));
670             removeCallbacks(mTransientFpErrorClearRunnable);
671             postDelayed(mTransientFpErrorClearRunnable, TRANSIENT_FP_ERROR_TIMEOUT);
672         }
673 
674         @Override
675         public void onFingerprintError(int msgId, String errString) {
676             if (!KeyguardUpdateMonitor.getInstance(mContext).isUnlockingWithFingerprintAllowed()
677                     || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
678                 return;
679             }
680             // TODO: Go to bouncer if this is "too many attempts" (lockout) error.
681             mIndicationController.showTransientIndication(errString,
682                     getResources().getColor(R.color.system_warning_color, null));
683             removeCallbacks(mHideTransientIndicationRunnable);
684             postDelayed(mHideTransientIndicationRunnable, 5000);
685         }
686     };
687 
setKeyguardIndicationController( KeyguardIndicationController keyguardIndicationController)688     public void setKeyguardIndicationController(
689             KeyguardIndicationController keyguardIndicationController) {
690         mIndicationController = keyguardIndicationController;
691     }
692 
setAssistManager(AssistManager assistManager)693     public void setAssistManager(AssistManager assistManager) {
694         mAssistManager = assistManager;
695         updateLeftAffordance();
696     }
697 
updateLeftAffordance()698     public void updateLeftAffordance() {
699         updateLeftAffordanceIcon();
700         updateLeftPreview();
701     }
702 }
703