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