Keyguard FP UX update

- Add scanning and error states to UI.
- Do not delay dismissing the panel when authenticating via
fingerprint.

Change-Id: I82e71c554c56e53ddf0677dca3e6909f7cedd59d
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index f75dd73..d2837b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1628,7 +1628,7 @@
 
                     // close the shade if it was open
                     animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
-                            true /* force */);
+                            true /* force */, true /* delayed */);
                     visibilityChanged(false);
 
                     return mIntent != null && mIntent.isActivity();
@@ -1640,6 +1640,9 @@
     public void animateCollapsePanels(int flags, boolean force) {
     }
 
+    public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
+    }
+
     public void overrideActivityPendingAppTransition(boolean keyguardShowing) {
         if (keyguardShowing) {
             try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index e2464c2..583184f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -34,6 +34,7 @@
 import android.view.animation.Interpolator;
 import android.widget.ImageView;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.KeyguardAffordanceHelper;
 
 /**
  * An ImageView which does not have overlapping renderings commands and therefore does not need a
@@ -75,6 +76,7 @@
     private float mCircleStartRadius;
     private float mMaxCircleSize;
     private Animator mPreviewClipper;
+    private float mRestingAlpha = KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT;
     private AnimatorListenerAdapter mClipEndListener = new AnimatorListenerAdapter() {
         @Override
         public void onAnimationEnd(Animator animation) {
@@ -394,6 +396,17 @@
         }
     }
 
+    public void setRestingAlpha(float alpha) {
+        mRestingAlpha = alpha;
+
+        // TODO: Handle the case an animation is playing.
+        setImageAlpha(alpha, false);
+    }
+
+    public float getRestingAlpha() {
+        return mRestingAlpha;
+    }
+
     public void setImageAlpha(float alpha, boolean animate) {
         setImageAlpha(alpha, animate, -1, null, null);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 58067c3..07a055c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.graphics.Color;
 import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.Handler;
@@ -53,6 +54,7 @@
 
     private String mRestingIndication;
     private String mTransientIndication;
+    private int mTransientTextColor;
     private boolean mVisible;
 
     private boolean mPowerPluggedIn;
@@ -105,7 +107,15 @@
      * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
      */
     public void showTransientIndication(String transientIndication) {
+        showTransientIndication(transientIndication, Color.WHITE);
+    }
+
+    /**
+     * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
+     */
+    public void showTransientIndication(String transientIndication, int textColor) {
         mTransientIndication = transientIndication;
+        mTransientTextColor = textColor;
         mHandler.removeMessages(MSG_HIDE_TRANSIENT);
         updateIndication();
     }
@@ -124,9 +134,17 @@
     private void updateIndication() {
         if (mVisible) {
             mTextView.switchIndication(computeIndication());
+            mTextView.setTextColor(computeColor());
         }
     }
 
+    private int computeColor() {
+        if (!TextUtils.isEmpty(mTransientIndication)) {
+            return mTransientTextColor;
+        }
+        return Color.WHITE;
+    }
+
     private String computeIndication() {
         if (!TextUtils.isEmpty(mTransientIndication)) {
             return mTransientIndication;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index cab152f..9d892f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -83,9 +83,9 @@
         mContext = context;
         mCallback = callback;
         initIcons();
-        updateIcon(mLeftIcon, 0.0f, SWIPE_RESTING_ALPHA_AMOUNT, false, false);
-        updateIcon(mCenterIcon, 0.0f, SWIPE_RESTING_ALPHA_AMOUNT, false, false);
-        updateIcon(mRightIcon, 0.0f, SWIPE_RESTING_ALPHA_AMOUNT, false, false);
+        updateIcon(mLeftIcon, 0.0f, mLeftIcon.getRestingAlpha(), false, false);
+        updateIcon(mCenterIcon, 0.0f, mCenterIcon.getRestingAlpha(), false, false);
+        updateIcon(mRightIcon, 0.0f, mRightIcon.getRestingAlpha(), false, false);
         initDimens();
     }
 
@@ -344,22 +344,23 @@
             float alpha = absTranslation / getMinTranslationAmount();
 
             // We interpolate the alpha of the other icons to 0
-            float fadeOutAlpha = SWIPE_RESTING_ALPHA_AMOUNT * (1.0f - alpha);
-            fadeOutAlpha = Math.max(0.0f, fadeOutAlpha);
-
-            // We interpolate the alpha of the targetView to 1
-            alpha = fadeOutAlpha + alpha;
+            float fadeOutAlpha = 1.0f - alpha;
+            fadeOutAlpha = Math.min(1.0f, Math.max(0.0f, fadeOutAlpha));
 
             boolean animateIcons = isReset && animateReset;
             float radius = getRadiusFromTranslation(absTranslation);
             boolean slowAnimation = isReset && isBelowFalsingThreshold();
             if (!isReset) {
-                updateIcon(targetView, radius, alpha, false, false);
+                updateIcon(targetView, radius, alpha + fadeOutAlpha * targetView.getRestingAlpha(),
+                        false, false);
             } else {
-                updateIcon(targetView, 0.0f, fadeOutAlpha, animateIcons, slowAnimation);
+                updateIcon(targetView, 0.0f, fadeOutAlpha * targetView.getRestingAlpha(),
+                        animateIcons, slowAnimation);
             }
-            updateIcon(otherView, 0.0f, fadeOutAlpha, animateIcons, slowAnimation);
-            updateIcon(mCenterIcon, 0.0f, fadeOutAlpha, animateIcons, slowAnimation);
+            updateIcon(otherView, 0.0f, fadeOutAlpha * otherView.getRestingAlpha(),
+                    animateIcons, slowAnimation);
+            updateIcon(mCenterIcon, 0.0f, fadeOutAlpha * mCenterIcon.getRestingAlpha(),
+                    animateIcons, slowAnimation);
 
             mTranslation = translation;
         }
@@ -369,15 +370,14 @@
         float alpha = newRadius / mMinBackgroundRadius;
 
         // We interpolate the alpha of the other icons to 0
-        float fadeOutAlpha = SWIPE_RESTING_ALPHA_AMOUNT * (1.0f - alpha);
+        float fadeOutAlpha =  1.0f - alpha;
         fadeOutAlpha = Math.max(0.0f, fadeOutAlpha);
 
         // We interpolate the alpha of the targetView to 1
-        alpha = fadeOutAlpha + alpha;
         KeyguardAffordanceView otherView = targetView == mRightIcon ? mLeftIcon : mRightIcon;
-        updateIconAlpha(targetView, alpha, false);
-        updateIconAlpha(otherView, fadeOutAlpha, false);
-        updateIconAlpha(mCenterIcon, fadeOutAlpha, false);
+        updateIconAlpha(targetView, alpha + fadeOutAlpha * targetView.getRestingAlpha(), false);
+        updateIconAlpha(otherView, fadeOutAlpha * otherView.getRestingAlpha(), false);
+        updateIconAlpha(mCenterIcon, fadeOutAlpha * mCenterIcon.getRestingAlpha(), false);
     }
 
     private float getTranslationFromRadius(float circleSize) {
@@ -404,14 +404,14 @@
     }
 
     private void updateIconAlpha(KeyguardAffordanceView view, float alpha, boolean animate) {
-        float scale = getScale(alpha);
+        float scale = getScale(alpha, view);
         alpha = Math.min(1.0f, alpha);
         view.setImageAlpha(alpha, animate);
         view.setImageScale(scale, animate);
     }
 
-    private float getScale(float alpha) {
-        float scale = alpha / SWIPE_RESTING_ALPHA_AMOUNT * 0.2f +
+    private float getScale(float alpha, KeyguardAffordanceView icon) {
+        float scale = alpha / icon.getRestingAlpha() * 0.2f +
                 KeyguardAffordanceView.MIN_ICON_SCALE_AMOUNT;
         return Math.min(scale, KeyguardAffordanceView.MAX_ICON_SCALE_AMOUNT);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 628ae84..410a7e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -25,13 +25,16 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.InsetDrawable;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.os.Vibrator;
 import android.provider.MediaStore;
+import android.provider.Settings;
 import android.telecom.TelecomManager;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -78,6 +81,9 @@
     private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL);
     private static final int DOZE_ANIMATION_STAGGER_DELAY = 48;
     private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250;
+    private static final long TRANSIENT_FP_ERROR_TIMEOUT = 1300;
+    private static final long[] FP_ERROR_VIBRATE_PATTERN = new long[] {0, 30, 100, 30};
+    private static final long[] FP_SUCCESS_VIBRATE_PATTERN = new long[] {0, 30};
 
     private KeyguardAffordanceView mCameraImageView;
     private KeyguardAffordanceView mPhoneImageView;
@@ -101,6 +107,7 @@
     private final Interpolator mLinearOutSlowInInterpolator;
     private int mLastUnlockIconRes = 0;
     private boolean mPrewarmSent;
+    private boolean mTransientFpError;
 
     public KeyguardBottomAreaView(Context context) {
         this(context, null);
@@ -431,28 +438,41 @@
             return;
         }
         // TODO: Real icon for facelock.
-        int iconRes = mUnlockMethodCache.isFaceUnlockRunning()
-                ? com.android.internal.R.drawable.ic_account_circle
+        boolean isFingerprintIcon =
+                KeyguardUpdateMonitor.getInstance(mContext).isFingerprintDetectionRunning();
+        boolean anyFingerprintIcon = isFingerprintIcon || mTransientFpError;
+        int iconRes = mTransientFpError ? R.drawable.ic_fingerprint_error
+                : isFingerprintIcon ? R.drawable.ic_fingerprint
+                : mUnlockMethodCache.isFaceUnlockRunning()
+                        ? com.android.internal.R.drawable.ic_account_circle
                 : mUnlockMethodCache.isCurrentlyInsecure() ? R.drawable.ic_lock_open_24dp
                 : R.drawable.ic_lock_24dp;
+
         if (mLastUnlockIconRes != iconRes) {
             Drawable icon = mContext.getDrawable(iconRes);
             int iconHeight = getResources().getDimensionPixelSize(
                     R.dimen.keyguard_affordance_icon_height);
             int iconWidth = getResources().getDimensionPixelSize(
                     R.dimen.keyguard_affordance_icon_width);
-            if (icon.getIntrinsicHeight() != iconHeight || icon.getIntrinsicWidth() != iconWidth) {
+            if (!anyFingerprintIcon && (icon.getIntrinsicHeight() != iconHeight
+                    || icon.getIntrinsicWidth() != iconWidth)) {
                 icon = new IntrinsicSizeDrawable(icon, iconWidth, iconHeight);
             }
             mLockIcon.setImageDrawable(icon);
+            mLockIcon.setPaddingRelative(0, 0, 0, anyFingerprintIcon
+                    ? getResources().getDimensionPixelSize(
+                            R.dimen.fingerprint_icon_additional_padding)
+                    : 0);
+            mLockIcon.setRestingAlpha(
+                    anyFingerprintIcon ? 1f : KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT);
         }
-        boolean trustManaged = mUnlockMethodCache.isTrustManaged();
+
+        // Hide trust circle when fingerprint is running.
+        boolean trustManaged = mUnlockMethodCache.isTrustManaged() && !anyFingerprintIcon;
         mTrustDrawable.setTrustManaged(trustManaged);
         updateLockIconClickability();
     }
 
-
-
     public KeyguardAffordanceView getPhoneView() {
         return mPhoneImageView;
     }
@@ -530,6 +550,14 @@
                 .setDuration(DOZE_ANIMATION_ELEMENT_DURATION);
     }
 
+    private void vibrateFingerprintError() {
+        mContext.getSystemService(Vibrator.class).vibrate(FP_ERROR_VIBRATE_PATTERN, -1);
+    }
+
+    private void vibrateFingerprintSuccess() {
+        mContext.getSystemService(Vibrator.class).vibrate(FP_SUCCESS_VIBRATE_PATTERN, -1);
+    }
+
     private final BroadcastReceiver mDevicePolicyReceiver = new BroadcastReceiver() {
         public void onReceive(Context context, Intent intent) {
             post(new Runnable() {
@@ -541,6 +569,15 @@
         }
     };
 
+    private final Runnable mTransientFpErrorClearRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mTransientFpError = false;
+            mIndicationController.hideTransientIndication();
+            updateLockIcon();
+        }
+    };
+
     private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
             new KeyguardUpdateMonitorCallback() {
         @Override
@@ -562,6 +599,33 @@
         public void onKeyguardVisibilityChanged(boolean showing) {
             updateLockIcon();
         }
+
+        @Override
+        public void onFingerprintAuthenticated(int userId) {
+            vibrateFingerprintSuccess();
+        }
+
+        @Override
+        public void onFingerprintRunningStateChanged(boolean running) {
+            updateLockIcon();
+        }
+
+        @Override
+        public void onFingerprintHelp(int msgId, String helpString) {
+            vibrateFingerprintError();
+            mTransientFpError = true;
+            mIndicationController.showTransientIndication(helpString,
+                    getResources().getColor(R.color.system_warning_color, null));
+            removeCallbacks(mTransientFpErrorClearRunnable);
+            postDelayed(mTransientFpErrorClearRunnable, TRANSIENT_FP_ERROR_TIMEOUT);
+            updateLockIcon();
+        }
+
+        @Override
+        public void onFingerprintError(int msgId, String errString) {
+            // TODO: Go to bouncer if this is "too many attempts" (lockout) error.
+            Log.i(TAG, "FP Error: " + errString);
+        }
     };
 
     public void setKeyguardIndicationController(
@@ -569,7 +633,6 @@
         mIndicationController = keyguardIndicationController;
     }
 
-
     /**
      * A wrapper around another Drawable that overrides the intrinsic size.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 216730b..02b6c19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -1505,7 +1505,7 @@
                 lockIcon.setImageScale(LOCK_ICON_ACTIVE_SCALE, true, 150,
                         mFastOutLinearInterpolator);
             } else if (!active && mUnlockIconActive && mTracking) {
-                lockIcon.setImageAlpha(KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT, true,
+                lockIcon.setImageAlpha(lockIcon.getRestingAlpha(), true /* animate */,
                         150, mFastOutLinearInterpolator, null);
                 lockIcon.setImageScale(1.0f, true, 150,
                         mFastOutLinearInterpolator);
@@ -1796,8 +1796,8 @@
                 mFastOutSlowInInterpolator, new Runnable() {
                     @Override
                     public void run() {
-                        icon.setImageAlpha(KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT,
-                                true, KeyguardAffordanceHelper.HINT_PHASE1_DURATION,
+                        icon.setImageAlpha(icon.getRestingAlpha(),
+                                true /* animate */, KeyguardAffordanceHelper.HINT_PHASE1_DURATION,
                                 mFastOutSlowInInterpolator, null);
                     }
                 });
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 3efaaff..c6e1be9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -187,11 +187,11 @@
                 (fullyOpenedPanel!=null)?" fullyOpened":"", fullyClosed?" fullyClosed":"");
     }
 
-    public void collapseAllPanels(boolean animate) {
+    public void collapseAllPanels(boolean animate, boolean delayed) {
         boolean waiting = false;
         for (PanelView pv : mPanels) {
             if (animate && !pv.isFullyCollapsed()) {
-                pv.collapse(true /* delayed */);
+                pv.collapse(delayed);
                 waiting = true;
             } else {
                 pv.resetViews();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index ad78f6a..a43110d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1991,10 +1991,14 @@
     }
 
     public void animateCollapsePanels(int flags) {
-        animateCollapsePanels(flags, false /* force */);
+        animateCollapsePanels(flags, false /* force */, false /* delayed */);
     }
 
     public void animateCollapsePanels(int flags, boolean force) {
+        animateCollapsePanels(flags, force, false /* delayed*/);
+    }
+
+    public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
         if (!force &&
                 (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
             runPostCollapseRunnables();
@@ -2018,7 +2022,7 @@
             mStatusBarWindowManager.setStatusBarFocusable(false);
 
             mStatusBarWindow.cancelExpandHelper();
-            mStatusBarView.collapseAllPanels(true);
+            mStatusBarView.collapseAllPanels(true /* animate */, delayed);
         }
     }
 
@@ -2061,7 +2065,7 @@
 
     public void animateCollapseQuickSettings() {
         if (mState == StatusBarState.SHADE) {
-            mStatusBarView.collapseAllPanels(true);
+            mStatusBarView.collapseAllPanels(true, false /* delayed */);
         }
     }
 
@@ -2074,7 +2078,7 @@
         }
 
         // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
-        mStatusBarView.collapseAllPanels(/*animate=*/ false);
+        mStatusBarView.collapseAllPanels(/*animate=*/ false, false /* delayed*/);
 
         // reset things to their proper state
         mStackScroller.setVisibility(View.VISIBLE);
@@ -2168,7 +2172,7 @@
             mStatusBarWindowState = state;
             if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state));
             if (!showing && mState == StatusBarState.SHADE) {
-                mStatusBarView.collapseAllPanels(false);
+                mStatusBarView.collapseAllPanels(false /* animate */, false /* delayed */);
             }
         }
         if (mNavigationBarView != null
@@ -2643,8 +2647,8 @@
                     }
                 });
                 if (dismissShade) {
-                    animateCollapsePanels(
-                            CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
+                    animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */,
+                            true /* delayed*/);
                 }
                 return true;
             }