• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.systemui.statusbar.phone;
18 
19 import android.content.Context;
20 import android.graphics.drawable.AnimatedVectorDrawable;
21 import android.graphics.drawable.Drawable;
22 import android.graphics.drawable.InsetDrawable;
23 import android.util.AttributeSet;
24 import android.view.View;
25 import android.view.accessibility.AccessibilityNodeInfo;
26 
27 import com.android.keyguard.KeyguardUpdateMonitor;
28 import com.android.systemui.R;
29 import com.android.systemui.statusbar.KeyguardAffordanceView;
30 import com.android.systemui.statusbar.policy.AccessibilityController;
31 
32 /**
33  * Manages the different states and animations of the unlock icon.
34  */
35 public class LockIcon extends KeyguardAffordanceView {
36 
37     private static final int STATE_LOCKED = 0;
38     private static final int STATE_LOCK_OPEN = 1;
39     private static final int STATE_FACE_UNLOCK = 2;
40     private static final int STATE_FINGERPRINT = 3;
41     private static final int STATE_FINGERPRINT_ERROR = 4;
42 
43     private int mLastState = 0;
44     private boolean mLastDeviceInteractive;
45     private boolean mTransientFpError;
46     private boolean mDeviceInteractive;
47     private boolean mScreenOn;
48     private boolean mLastScreenOn;
49     private final TrustDrawable mTrustDrawable;
50     private final UnlockMethodCache mUnlockMethodCache;
51     private AccessibilityController mAccessibilityController;
52     private boolean mHasFingerPrintIcon;
53 
LockIcon(Context context, AttributeSet attrs)54     public LockIcon(Context context, AttributeSet attrs) {
55         super(context, attrs);
56         mTrustDrawable = new TrustDrawable(context);
57         setBackground(mTrustDrawable);
58         mUnlockMethodCache = UnlockMethodCache.getInstance(context);
59     }
60 
61     @Override
onVisibilityChanged(View changedView, int visibility)62     protected void onVisibilityChanged(View changedView, int visibility) {
63         super.onVisibilityChanged(changedView, visibility);
64         if (isShown()) {
65             mTrustDrawable.start();
66         } else {
67             mTrustDrawable.stop();
68         }
69     }
70 
71     @Override
onDetachedFromWindow()72     protected void onDetachedFromWindow() {
73         super.onDetachedFromWindow();
74         mTrustDrawable.stop();
75     }
76 
setTransientFpError(boolean transientFpError)77     public void setTransientFpError(boolean transientFpError) {
78         mTransientFpError = transientFpError;
79         update();
80     }
81 
setDeviceInteractive(boolean deviceInteractive)82     public void setDeviceInteractive(boolean deviceInteractive) {
83         mDeviceInteractive = deviceInteractive;
84         update();
85     }
86 
setScreenOn(boolean screenOn)87     public void setScreenOn(boolean screenOn) {
88         mScreenOn = screenOn;
89         update();
90     }
91 
update()92     public void update() {
93         boolean visible = isShown()
94                 && KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive();
95         if (visible) {
96             mTrustDrawable.start();
97         } else {
98             mTrustDrawable.stop();
99         }
100         // TODO: Real icon for facelock.
101         int state = getState();
102         boolean anyFingerprintIcon = state == STATE_FINGERPRINT || state == STATE_FINGERPRINT_ERROR;
103         boolean useAdditionalPadding = anyFingerprintIcon;
104         boolean trustHidden = anyFingerprintIcon;
105         if (state != mLastState || mDeviceInteractive != mLastDeviceInteractive
106                 || mScreenOn != mLastScreenOn) {
107             boolean isAnim = true;
108             int iconRes = getAnimationResForTransition(mLastState, state, mLastDeviceInteractive,
109                     mDeviceInteractive, mLastScreenOn, mScreenOn);
110             if (iconRes == R.drawable.lockscreen_fingerprint_draw_off_animation) {
111                 anyFingerprintIcon = true;
112                 useAdditionalPadding = true;
113                 trustHidden = true;
114             } else if (iconRes == R.drawable.trusted_state_to_error_animation) {
115                 anyFingerprintIcon = true;
116                 useAdditionalPadding = false;
117                 trustHidden = true;
118             } else if (iconRes == R.drawable.error_to_trustedstate_animation) {
119                 anyFingerprintIcon = true;
120                 useAdditionalPadding = false;
121                 trustHidden = false;
122             }
123             if (iconRes == -1) {
124                 iconRes = getIconForState(state, mScreenOn, mDeviceInteractive);
125                 isAnim = false;
126             }
127             Drawable icon = mContext.getDrawable(iconRes);
128             final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
129                     ? (AnimatedVectorDrawable) icon
130                     : null;
131             int iconHeight = getResources().getDimensionPixelSize(
132                     R.dimen.keyguard_affordance_icon_height);
133             int iconWidth = getResources().getDimensionPixelSize(
134                     R.dimen.keyguard_affordance_icon_width);
135             if (!anyFingerprintIcon && (icon.getIntrinsicHeight() != iconHeight
136                     || icon.getIntrinsicWidth() != iconWidth)) {
137                 icon = new IntrinsicSizeDrawable(icon, iconWidth, iconHeight);
138             }
139             setPaddingRelative(0, 0, 0, useAdditionalPadding
140                     ? getResources().getDimensionPixelSize(
141                     R.dimen.fingerprint_icon_additional_padding)
142                     : 0);
143             setRestingAlpha(
144                     anyFingerprintIcon ? 1f : KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT);
145             setImageDrawable(icon);
146             String contentDescription = getResources().getString(anyFingerprintIcon
147                     ? R.string.accessibility_unlock_button_fingerprint
148                     : R.string.accessibility_unlock_button);
149             setContentDescription(contentDescription);
150             mHasFingerPrintIcon = anyFingerprintIcon;
151             if (animation != null && isAnim) {
152                 animation.start();
153             }
154             mLastState = state;
155             mLastDeviceInteractive = mDeviceInteractive;
156             mLastScreenOn = mScreenOn;
157         }
158 
159         // Hide trust circle when fingerprint is running.
160         boolean trustManaged = mUnlockMethodCache.isTrustManaged() && !trustHidden;
161         mTrustDrawable.setTrustManaged(trustManaged);
162         updateClickability();
163     }
164 
updateClickability()165     private void updateClickability() {
166         if (mAccessibilityController == null) {
167             return;
168         }
169         boolean clickToUnlock = mAccessibilityController.isTouchExplorationEnabled();
170         boolean clickToForceLock = mUnlockMethodCache.isTrustManaged()
171                 && !mAccessibilityController.isAccessibilityEnabled();
172         boolean longClickToForceLock = mUnlockMethodCache.isTrustManaged()
173                 && !clickToForceLock;
174         setClickable(clickToForceLock || clickToUnlock);
175         setLongClickable(longClickToForceLock);
176         setFocusable(mAccessibilityController.isAccessibilityEnabled());
177     }
178 
179     @Override
onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)180     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
181         super.onInitializeAccessibilityNodeInfo(info);
182         if (mHasFingerPrintIcon) {
183             // Avoid that the button description is also spoken
184             info.setClassName(LockIcon.class.getName());
185             AccessibilityNodeInfo.AccessibilityAction unlock
186                     = new AccessibilityNodeInfo.AccessibilityAction(
187                     AccessibilityNodeInfo.ACTION_CLICK,
188                     getContext().getString(R.string.accessibility_unlock_without_fingerprint));
189             info.addAction(unlock);
190         }
191     }
192 
setAccessibilityController(AccessibilityController accessibilityController)193     public void setAccessibilityController(AccessibilityController accessibilityController) {
194         mAccessibilityController = accessibilityController;
195     }
196 
getIconForState(int state, boolean screenOn, boolean deviceInteractive)197     private int getIconForState(int state, boolean screenOn, boolean deviceInteractive) {
198         switch (state) {
199             case STATE_LOCKED:
200                 return R.drawable.ic_lock_24dp;
201             case STATE_LOCK_OPEN:
202                 return R.drawable.ic_lock_open_24dp;
203             case STATE_FACE_UNLOCK:
204                 return com.android.internal.R.drawable.ic_account_circle;
205             case STATE_FINGERPRINT:
206                 // If screen is off and device asleep, use the draw on animation so the first frame
207                 // gets drawn.
208                 return screenOn && deviceInteractive
209                         ? R.drawable.ic_fingerprint
210                         : R.drawable.lockscreen_fingerprint_draw_on_animation;
211             case STATE_FINGERPRINT_ERROR:
212                 return R.drawable.ic_fingerprint_error;
213             default:
214                 throw new IllegalArgumentException();
215         }
216     }
217 
getAnimationResForTransition(int oldState, int newState, boolean oldDeviceInteractive, boolean deviceInteractive, boolean oldScreenOn, boolean screenOn)218     private int getAnimationResForTransition(int oldState, int newState,
219             boolean oldDeviceInteractive, boolean deviceInteractive,
220             boolean oldScreenOn, boolean screenOn) {
221         if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_ERROR) {
222             return R.drawable.lockscreen_fingerprint_fp_to_error_state_animation;
223         } else if (oldState == STATE_LOCK_OPEN && newState == STATE_FINGERPRINT_ERROR) {
224             return R.drawable.trusted_state_to_error_animation;
225         } else if (oldState == STATE_FINGERPRINT_ERROR && newState == STATE_LOCK_OPEN) {
226             return R.drawable.error_to_trustedstate_animation;
227         } else if (oldState == STATE_FINGERPRINT_ERROR && newState == STATE_FINGERPRINT) {
228             return R.drawable.lockscreen_fingerprint_error_state_to_fp_animation;
229         } else if (oldState == STATE_FINGERPRINT && newState == STATE_LOCK_OPEN
230                 && !mUnlockMethodCache.isTrusted()) {
231             return R.drawable.lockscreen_fingerprint_draw_off_animation;
232         } else if (newState == STATE_FINGERPRINT && (!oldScreenOn && screenOn && deviceInteractive
233                 || screenOn && !oldDeviceInteractive && deviceInteractive)) {
234             return R.drawable.lockscreen_fingerprint_draw_on_animation;
235         } else {
236             return -1;
237         }
238     }
239 
getState()240     private int getState() {
241         KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
242         boolean fingerprintRunning = updateMonitor.isFingerprintDetectionRunning();
243         boolean unlockingAllowed = updateMonitor.isUnlockingWithFingerprintAllowed();
244         if (mTransientFpError) {
245             return STATE_FINGERPRINT_ERROR;
246         } else if (mUnlockMethodCache.canSkipBouncer()) {
247             return STATE_LOCK_OPEN;
248         } else if (mUnlockMethodCache.isFaceUnlockRunning()) {
249             return STATE_FACE_UNLOCK;
250         } else if (fingerprintRunning && unlockingAllowed) {
251             return STATE_FINGERPRINT;
252         } else {
253             return STATE_LOCKED;
254         }
255     }
256 
257     /**
258      * A wrapper around another Drawable that overrides the intrinsic size.
259      */
260     private static class IntrinsicSizeDrawable extends InsetDrawable {
261 
262         private final int mIntrinsicWidth;
263         private final int mIntrinsicHeight;
264 
IntrinsicSizeDrawable(Drawable drawable, int intrinsicWidth, int intrinsicHeight)265         public IntrinsicSizeDrawable(Drawable drawable, int intrinsicWidth, int intrinsicHeight) {
266             super(drawable, 0);
267             mIntrinsicWidth = intrinsicWidth;
268             mIntrinsicHeight = intrinsicHeight;
269         }
270 
271         @Override
getIntrinsicWidth()272         public int getIntrinsicWidth() {
273             return mIntrinsicWidth;
274         }
275 
276         @Override
getIntrinsicHeight()277         public int getIntrinsicHeight() {
278             return mIntrinsicHeight;
279         }
280     }
281 }
282