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