blob: 264f5749fa7358ea60669c4b001565ff4218a2cc [file] [log] [blame]
Jorim Jaggi27267d62015-04-28 13:27:12 -07001/*
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
17package com.android.systemui.statusbar.phone;
18
Jorim Jaggi27267d62015-04-28 13:27:12 -070019import android.content.Context;
Selim Cinek6ebba592016-05-31 15:28:28 -070020import android.content.res.Configuration;
Jorim Jaggi27267d62015-04-28 13:27:12 -070021import android.graphics.drawable.AnimatedVectorDrawable;
22import android.graphics.drawable.Drawable;
23import android.graphics.drawable.InsetDrawable;
24import android.util.AttributeSet;
25import android.view.View;
Selim Cinekc99d9a952015-06-19 18:44:50 -070026import android.view.accessibility.AccessibilityNodeInfo;
Jorim Jaggi27267d62015-04-28 13:27:12 -070027
28import com.android.keyguard.KeyguardUpdateMonitor;
29import com.android.systemui.R;
30import com.android.systemui.statusbar.KeyguardAffordanceView;
31import com.android.systemui.statusbar.policy.AccessibilityController;
Zachary Iqbalf50284c2017-01-22 18:54:46 -080032import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
Jorim Jaggi27267d62015-04-28 13:27:12 -070033
34/**
35 * Manages the different states and animations of the unlock icon.
36 */
Zachary Iqbalf50284c2017-01-22 18:54:46 -080037public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChangedListener {
Jorim Jaggi27267d62015-04-28 13:27:12 -070038
Jorim Jaggi8dea48c2016-11-28 14:47:45 +010039 private static final int FP_DRAW_OFF_TIMEOUT = 800;
40
Jorim Jaggi27267d62015-04-28 13:27:12 -070041 private static final int STATE_LOCKED = 0;
42 private static final int STATE_LOCK_OPEN = 1;
43 private static final int STATE_FACE_UNLOCK = 2;
44 private static final int STATE_FINGERPRINT = 3;
45 private static final int STATE_FINGERPRINT_ERROR = 4;
46
47 private int mLastState = 0;
Jorim Jaggi0d210f62015-07-10 14:24:44 -070048 private boolean mLastDeviceInteractive;
Jorim Jaggi27267d62015-04-28 13:27:12 -070049 private boolean mTransientFpError;
Jorim Jaggi0d210f62015-07-10 14:24:44 -070050 private boolean mDeviceInteractive;
Jorim Jaggif1518da2015-07-30 11:56:36 -070051 private boolean mScreenOn;
52 private boolean mLastScreenOn;
Zachary Iqbalf50284c2017-01-22 18:54:46 -080053 private Drawable mUserAvatarIcon;
Selim Cinek6ebba592016-05-31 15:28:28 -070054 private TrustDrawable mTrustDrawable;
Jorim Jaggi27267d62015-04-28 13:27:12 -070055 private final UnlockMethodCache mUnlockMethodCache;
56 private AccessibilityController mAccessibilityController;
Selim Cinekc99d9a952015-06-19 18:44:50 -070057 private boolean mHasFingerPrintIcon;
Selim Cinek6ebba592016-05-31 15:28:28 -070058 private int mDensity;
Jorim Jaggi27267d62015-04-28 13:27:12 -070059
Jorim Jaggi8dea48c2016-11-28 14:47:45 +010060 private final Runnable mDrawOffTimeout = () -> update(true /* forceUpdate */);
61
Jorim Jaggi582b57f2015-04-28 16:28:26 -070062 public LockIcon(Context context, AttributeSet attrs) {
Jorim Jaggi27267d62015-04-28 13:27:12 -070063 super(context, attrs);
64 mTrustDrawable = new TrustDrawable(context);
65 setBackground(mTrustDrawable);
66 mUnlockMethodCache = UnlockMethodCache.getInstance(context);
67 }
68
69 @Override
Jorim Jaggi582b57f2015-04-28 16:28:26 -070070 protected void onVisibilityChanged(View changedView, int visibility) {
Jorim Jaggi27267d62015-04-28 13:27:12 -070071 super.onVisibilityChanged(changedView, visibility);
72 if (isShown()) {
73 mTrustDrawable.start();
74 } else {
75 mTrustDrawable.stop();
76 }
77 }
78
79 @Override
80 protected void onDetachedFromWindow() {
81 super.onDetachedFromWindow();
82 mTrustDrawable.stop();
83 }
84
Zachary Iqbalf50284c2017-01-22 18:54:46 -080085 @Override
86 public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
87 mUserAvatarIcon = picture;
88 update();
89 }
90
Jorim Jaggi27267d62015-04-28 13:27:12 -070091 public void setTransientFpError(boolean transientFpError) {
92 mTransientFpError = transientFpError;
93 update();
94 }
95
Jorim Jaggi0d210f62015-07-10 14:24:44 -070096 public void setDeviceInteractive(boolean deviceInteractive) {
97 mDeviceInteractive = deviceInteractive;
Jorim Jaggi864e64b2015-05-20 14:13:23 -070098 update();
99 }
100
Jorim Jaggif1518da2015-07-30 11:56:36 -0700101 public void setScreenOn(boolean screenOn) {
102 mScreenOn = screenOn;
103 update();
104 }
105
Selim Cinek6ebba592016-05-31 15:28:28 -0700106 @Override
107 protected void onConfigurationChanged(Configuration newConfig) {
108 super.onConfigurationChanged(newConfig);
109 final int density = newConfig.densityDpi;
110 if (density != mDensity) {
111 mDensity = density;
112 mTrustDrawable.stop();
113 mTrustDrawable = new TrustDrawable(getContext());
114 setBackground(mTrustDrawable);
115 update();
116 }
117 }
118
Jorim Jaggi27267d62015-04-28 13:27:12 -0700119 public void update() {
Selim Cinek6ebba592016-05-31 15:28:28 -0700120 update(false /* force */);
121 }
122
123 public void update(boolean force) {
Jorim Jaggi0d210f62015-07-10 14:24:44 -0700124 boolean visible = isShown()
125 && KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive();
Jorim Jaggi27267d62015-04-28 13:27:12 -0700126 if (visible) {
127 mTrustDrawable.start();
128 } else {
129 mTrustDrawable.stop();
130 }
Jorim Jaggi27267d62015-04-28 13:27:12 -0700131 int state = getState();
132 boolean anyFingerprintIcon = state == STATE_FINGERPRINT || state == STATE_FINGERPRINT_ERROR;
Selim Cinekc3841982015-08-25 18:34:29 -0700133 boolean useAdditionalPadding = anyFingerprintIcon;
134 boolean trustHidden = anyFingerprintIcon;
Jorim Jaggif1518da2015-07-30 11:56:36 -0700135 if (state != mLastState || mDeviceInteractive != mLastDeviceInteractive
Selim Cinek6ebba592016-05-31 15:28:28 -0700136 || mScreenOn != mLastScreenOn || force) {
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800137 int iconAnimRes =
138 getAnimationResForTransition(mLastState, state, mLastDeviceInteractive,
Jorim Jaggif1518da2015-07-30 11:56:36 -0700139 mDeviceInteractive, mLastScreenOn, mScreenOn);
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800140 boolean isAnim = iconAnimRes != -1;
141 if (iconAnimRes == R.drawable.lockscreen_fingerprint_draw_off_animation) {
Jorim Jaggi864e64b2015-05-20 14:13:23 -0700142 anyFingerprintIcon = true;
Selim Cinekc3841982015-08-25 18:34:29 -0700143 useAdditionalPadding = true;
144 trustHidden = true;
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800145 } else if (iconAnimRes == R.drawable.trusted_state_to_error_animation) {
Selim Cinekc3841982015-08-25 18:34:29 -0700146 anyFingerprintIcon = true;
147 useAdditionalPadding = false;
148 trustHidden = true;
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800149 } else if (iconAnimRes == R.drawable.error_to_trustedstate_animation) {
Selim Cinekc3841982015-08-25 18:34:29 -0700150 anyFingerprintIcon = true;
151 useAdditionalPadding = false;
152 trustHidden = false;
Jorim Jaggi864e64b2015-05-20 14:13:23 -0700153 }
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800154
155 Drawable icon;
156 if (isAnim) {
157 // Load the animation resource.
158 icon = mContext.getDrawable(iconAnimRes);
159 } else {
160 // Load the static icon resource based on the current state.
161 icon = getIconForState(state, mScreenOn, mDeviceInteractive);
Jorim Jaggi27267d62015-04-28 13:27:12 -0700162 }
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800163
Jorim Jaggi864e64b2015-05-20 14:13:23 -0700164 final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
165 ? (AnimatedVectorDrawable) icon
166 : null;
Jorim Jaggi27267d62015-04-28 13:27:12 -0700167 int iconHeight = getResources().getDimensionPixelSize(
168 R.dimen.keyguard_affordance_icon_height);
169 int iconWidth = getResources().getDimensionPixelSize(
170 R.dimen.keyguard_affordance_icon_width);
171 if (!anyFingerprintIcon && (icon.getIntrinsicHeight() != iconHeight
172 || icon.getIntrinsicWidth() != iconWidth)) {
173 icon = new IntrinsicSizeDrawable(icon, iconWidth, iconHeight);
174 }
Selim Cinekc3841982015-08-25 18:34:29 -0700175 setPaddingRelative(0, 0, 0, useAdditionalPadding
Jorim Jaggi27267d62015-04-28 13:27:12 -0700176 ? getResources().getDimensionPixelSize(
177 R.dimen.fingerprint_icon_additional_padding)
178 : 0);
179 setRestingAlpha(
180 anyFingerprintIcon ? 1f : KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT);
Evan Lairdb0ddf872017-07-24 15:52:59 -0400181 setImageDrawable(icon, false);
Selim Cinekc99d9a952015-06-19 18:44:50 -0700182 mHasFingerPrintIcon = anyFingerprintIcon;
Jorim Jaggif1518da2015-07-30 11:56:36 -0700183 if (animation != null && isAnim) {
Jorim Jaggi23bb84b2016-06-30 19:11:38 -0700184 animation.forceAnimationOnUI();
Jorim Jaggif1518da2015-07-30 11:56:36 -0700185 animation.start();
Jorim Jaggi27267d62015-04-28 13:27:12 -0700186 }
Jorim Jaggi8dea48c2016-11-28 14:47:45 +0100187
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800188 if (iconAnimRes == R.drawable.lockscreen_fingerprint_draw_off_animation) {
Jorim Jaggi8dea48c2016-11-28 14:47:45 +0100189 removeCallbacks(mDrawOffTimeout);
190 postDelayed(mDrawOffTimeout, FP_DRAW_OFF_TIMEOUT);
191 } else {
192 removeCallbacks(mDrawOffTimeout);
193 }
194
Jorim Jaggi864e64b2015-05-20 14:13:23 -0700195 mLastState = state;
Jorim Jaggi0d210f62015-07-10 14:24:44 -0700196 mLastDeviceInteractive = mDeviceInteractive;
Jorim Jaggif1518da2015-07-30 11:56:36 -0700197 mLastScreenOn = mScreenOn;
Jorim Jaggi27267d62015-04-28 13:27:12 -0700198 }
199
200 // Hide trust circle when fingerprint is running.
Selim Cinekc3841982015-08-25 18:34:29 -0700201 boolean trustManaged = mUnlockMethodCache.isTrustManaged() && !trustHidden;
Jorim Jaggi27267d62015-04-28 13:27:12 -0700202 mTrustDrawable.setTrustManaged(trustManaged);
Jorim Jaggi27267d62015-04-28 13:27:12 -0700203 updateClickability();
204 }
205
206 private void updateClickability() {
207 if (mAccessibilityController == null) {
208 return;
209 }
Adrian Roosa94e9642017-08-17 17:29:19 +0200210 boolean clickToUnlock = mAccessibilityController.isAccessibilityEnabled();
Jorim Jaggi16093fe32015-09-09 23:29:17 +0000211 boolean clickToForceLock = mUnlockMethodCache.isTrustManaged()
Adrian Roosa94e9642017-08-17 17:29:19 +0200212 && !clickToUnlock;
Jorim Jaggi16093fe32015-09-09 23:29:17 +0000213 boolean longClickToForceLock = mUnlockMethodCache.isTrustManaged()
Jorim Jaggi27267d62015-04-28 13:27:12 -0700214 && !clickToForceLock;
215 setClickable(clickToForceLock || clickToUnlock);
216 setLongClickable(longClickToForceLock);
217 setFocusable(mAccessibilityController.isAccessibilityEnabled());
218 }
219
Selim Cinekc99d9a952015-06-19 18:44:50 -0700220 @Override
221 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
222 super.onInitializeAccessibilityNodeInfo(info);
223 if (mHasFingerPrintIcon) {
Selim Cinekc99d9a952015-06-19 18:44:50 -0700224 AccessibilityNodeInfo.AccessibilityAction unlock
225 = new AccessibilityNodeInfo.AccessibilityAction(
226 AccessibilityNodeInfo.ACTION_CLICK,
227 getContext().getString(R.string.accessibility_unlock_without_fingerprint));
228 info.addAction(unlock);
Selim Cinek947c77c2017-03-23 14:20:32 -0700229 info.setHintText(getContext().getString(
230 R.string.accessibility_waiting_for_fingerprint));
Selim Cinekc99d9a952015-06-19 18:44:50 -0700231 }
232 }
233
Jorim Jaggi27267d62015-04-28 13:27:12 -0700234 public void setAccessibilityController(AccessibilityController accessibilityController) {
235 mAccessibilityController = accessibilityController;
236 }
237
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800238 private Drawable getIconForState(int state, boolean screenOn, boolean deviceInteractive) {
239 int iconRes;
Jorim Jaggi27267d62015-04-28 13:27:12 -0700240 switch (state) {
241 case STATE_LOCKED:
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800242 iconRes = R.drawable.ic_lock_24dp;
243 break;
Jorim Jaggi27267d62015-04-28 13:27:12 -0700244 case STATE_LOCK_OPEN:
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800245 if (mUnlockMethodCache.isTrustManaged() && mUnlockMethodCache.isTrusted()
246 && mUserAvatarIcon != null) {
247 return mUserAvatarIcon;
248 } else {
249 iconRes = R.drawable.ic_lock_open_24dp;
250 }
251 break;
Jorim Jaggi27267d62015-04-28 13:27:12 -0700252 case STATE_FACE_UNLOCK:
Lucas Dupin334f9542018-01-16 11:24:46 -0800253 iconRes = R.drawable.ic_face_unlock;
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800254 break;
Jorim Jaggi27267d62015-04-28 13:27:12 -0700255 case STATE_FINGERPRINT:
Jorim Jaggif1518da2015-07-30 11:56:36 -0700256 // If screen is off and device asleep, use the draw on animation so the first frame
257 // gets drawn.
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800258 iconRes = screenOn && deviceInteractive
Jorim Jaggif1518da2015-07-30 11:56:36 -0700259 ? R.drawable.ic_fingerprint
260 : R.drawable.lockscreen_fingerprint_draw_on_animation;
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800261 break;
Jorim Jaggi27267d62015-04-28 13:27:12 -0700262 case STATE_FINGERPRINT_ERROR:
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800263 iconRes = R.drawable.ic_fingerprint_error;
264 break;
Jorim Jaggi27267d62015-04-28 13:27:12 -0700265 default:
266 throw new IllegalArgumentException();
267 }
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800268
269 return mContext.getDrawable(iconRes);
Jorim Jaggi27267d62015-04-28 13:27:12 -0700270 }
271
Jorim Jaggif1518da2015-07-30 11:56:36 -0700272 private int getAnimationResForTransition(int oldState, int newState,
273 boolean oldDeviceInteractive, boolean deviceInteractive,
274 boolean oldScreenOn, boolean screenOn) {
Jorim Jaggi27267d62015-04-28 13:27:12 -0700275 if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_ERROR) {
Jorim Jaggi864e64b2015-05-20 14:13:23 -0700276 return R.drawable.lockscreen_fingerprint_fp_to_error_state_animation;
Selim Cinekc3841982015-08-25 18:34:29 -0700277 } else if (oldState == STATE_LOCK_OPEN && newState == STATE_FINGERPRINT_ERROR) {
278 return R.drawable.trusted_state_to_error_animation;
279 } else if (oldState == STATE_FINGERPRINT_ERROR && newState == STATE_LOCK_OPEN) {
280 return R.drawable.error_to_trustedstate_animation;
Jorim Jaggi864e64b2015-05-20 14:13:23 -0700281 } else if (oldState == STATE_FINGERPRINT_ERROR && newState == STATE_FINGERPRINT) {
282 return R.drawable.lockscreen_fingerprint_error_state_to_fp_animation;
Selim Cinek2a46d682015-06-25 19:44:05 -0400283 } else if (oldState == STATE_FINGERPRINT && newState == STATE_LOCK_OPEN
Selim Cineke8bae622015-07-15 13:24:06 -0700284 && !mUnlockMethodCache.isTrusted()) {
Jorim Jaggi864e64b2015-05-20 14:13:23 -0700285 return R.drawable.lockscreen_fingerprint_draw_off_animation;
Jorim Jaggif1518da2015-07-30 11:56:36 -0700286 } else if (newState == STATE_FINGERPRINT && (!oldScreenOn && screenOn && deviceInteractive
287 || screenOn && !oldDeviceInteractive && deviceInteractive)) {
Jorim Jaggi864e64b2015-05-20 14:13:23 -0700288 return R.drawable.lockscreen_fingerprint_draw_on_animation;
Jorim Jaggi27267d62015-04-28 13:27:12 -0700289 } else {
290 return -1;
291 }
292 }
293
294 private int getState() {
Selim Cinek1fcafc42015-07-20 14:39:25 -0700295 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
296 boolean fingerprintRunning = updateMonitor.isFingerprintDetectionRunning();
297 boolean unlockingAllowed = updateMonitor.isUnlockingWithFingerprintAllowed();
Selim Cinekc3841982015-08-25 18:34:29 -0700298 if (mTransientFpError) {
Jorim Jaggi27267d62015-04-28 13:27:12 -0700299 return STATE_FINGERPRINT_ERROR;
Selim Cinekc3841982015-08-25 18:34:29 -0700300 } else if (mUnlockMethodCache.canSkipBouncer()) {
301 return STATE_LOCK_OPEN;
Jorim Jaggi27267d62015-04-28 13:27:12 -0700302 } else if (mUnlockMethodCache.isFaceUnlockRunning()) {
303 return STATE_FACE_UNLOCK;
Adrian Roose07cbaf2015-09-02 13:29:42 -0700304 } else if (fingerprintRunning && unlockingAllowed) {
305 return STATE_FINGERPRINT;
Jorim Jaggi27267d62015-04-28 13:27:12 -0700306 } else {
307 return STATE_LOCKED;
308 }
309 }
310
311 /**
312 * A wrapper around another Drawable that overrides the intrinsic size.
313 */
314 private static class IntrinsicSizeDrawable extends InsetDrawable {
315
316 private final int mIntrinsicWidth;
317 private final int mIntrinsicHeight;
318
319 public IntrinsicSizeDrawable(Drawable drawable, int intrinsicWidth, int intrinsicHeight) {
320 super(drawable, 0);
321 mIntrinsicWidth = intrinsicWidth;
322 mIntrinsicHeight = intrinsicHeight;
323 }
324
325 @Override
326 public int getIntrinsicWidth() {
327 return mIntrinsicWidth;
328 }
329
330 @Override
331 public int getIntrinsicHeight() {
332 return mIntrinsicHeight;
333 }
334 }
335}