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