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