summaryrefslogtreecommitdiffstats
path: root/packages/SystemUI
diff options
context:
space:
mode:
authorAdrian Roos <roosa@google.com>2014-08-19 22:23:37 +0200
committerAdrian Roos <roosa@google.com>2014-08-20 18:54:25 +0200
commit2e3ccbb37e21eb40308f83fb185209773147f78d (patch)
treef6878d92c5d31eb9156ffd0e88bad5b6587b0feb /packages/SystemUI
parent03861d072131563561bb5873d35dc346e82bd904 (diff)
downloadframeworks_base-2e3ccbb37e21eb40308f83fb185209773147f78d.zip
frameworks_base-2e3ccbb37e21eb40308f83fb185209773147f78d.tar.gz
frameworks_base-2e3ccbb37e21eb40308f83fb185209773147f78d.tar.bz2
Implement trust managed visuals
Updates the trust managed indicator to match the speced visuals and behavior. Bug: 15519687 Change-Id: I2be04e8b37f3b5d9865ae4d25b46222cafadd113
Diffstat (limited to 'packages/SystemUI')
-rw-r--r--packages/SystemUI/res/drawable/trust_circle.xml22
-rw-r--r--packages/SystemUI/res/values/dimens.xml12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java297
4 files changed, 347 insertions, 28 deletions
diff --git a/packages/SystemUI/res/drawable/trust_circle.xml b/packages/SystemUI/res/drawable/trust_circle.xml
deleted file mode 100644
index 56fc62e..0000000
--- a/packages/SystemUI/res/drawable/trust_circle.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2014 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="ring"
- android:innerRadius="22dp" android:thickness="2dp">
- <solid android:color="#4cffffff" />
-</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5415d19..5fcfa2e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -430,4 +430,16 @@
<!-- The text size for battery level -->
<dimen name="battery_level_text_size">12sp</dimen>
+
+ <!-- TrustDrawable: Minimum inner radius of the breathing animation -->
+ <dimen name="trust_circle_inner_radius_visible_min">22dp</dimen>
+ <!-- TrustDrawable: Maximum inner radius of the breathing animation -->
+ <dimen name="trust_circle_inner_radius_visible_max">24dp</dimen>
+ <!-- TrustDrawable: Inner radius at the end of the exit animation -->
+ <dimen name="trust_circle_inner_radius_exit">38dp</dimen>
+ <!-- TrustDrawable: Inner radius at the beginning of the enter animation -->
+ <dimen name="trust_circle_inner_radius_enter">18dp</dimen>
+ <!-- TrustDrawable: Thickness of the circle -->
+ <dimen name="trust_circle_thickness">2dp</dimen>
+
</resources>
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 3e2a398..de42fe1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -82,21 +82,24 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
private KeyguardIndicationController mIndicationController;
private boolean mFaceUnlockRunning;
+ private final TrustDrawable mTrustDrawable;
+
public KeyguardBottomAreaView(Context context) {
- super(context);
+ this(context, null);
}
public KeyguardBottomAreaView(Context context, AttributeSet attrs) {
- super(context, attrs);
+ this(context, attrs, 0);
}
public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
+ this(context, attrs, defStyleAttr, 0);
}
public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mTrustDrawable = new TrustDrawable(mContext);
}
@Override
@@ -120,6 +123,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mPreviewInflater = new PreviewInflater(mContext, new LockPatternUtils(mContext));
inflatePreviews();
mLockIcon.setOnClickListener(this);
+ mLockIcon.setBackground(mTrustDrawable);
}
@Override
@@ -267,14 +271,31 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
+ if (isShown()) {
+ mTrustDrawable.start();
+ } else {
+ mTrustDrawable.stop();
+ }
if (changedView == this && visibility == VISIBLE) {
updateLockIcon();
updateCameraVisibility();
}
}
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mTrustDrawable.stop();
+ }
+
private void updateLockIcon() {
- if (getVisibility() != VISIBLE) {
+ boolean visible = isShown() && KeyguardUpdateMonitor.getInstance(mContext).isScreenOn();
+ if (visible) {
+ mTrustDrawable.start();
+ } else {
+ mTrustDrawable.stop();
+ }
+ if (!visible) {
return;
}
// TODO: Real icon for facelock.
@@ -283,11 +304,12 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
: R.drawable.ic_lock_24dp;
mLockIcon.setImageResource(iconRes);
boolean trustManaged = mUnlockMethodCache.isTrustManaged();
- mLockIcon.setBackgroundResource(trustManaged && !mFaceUnlockRunning
- ? R.drawable.trust_circle : 0);
+ mTrustDrawable.setTrustManaged(trustManaged);
mLockIcon.setClickable(trustManaged);
}
+
+
public KeyguardAffordanceView getPhoneView() {
return mPhoneImageView;
}
@@ -359,6 +381,16 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mFaceUnlockRunning = running;
updateLockIcon();
}
+
+ @Override
+ public void onScreenTurnedOn() {
+ updateLockIcon();
+ }
+
+ @Override
+ public void onScreenTurnedOff(int why) {
+ updateLockIcon();
+ }
};
public void setKeyguardIndicationController(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java
new file mode 100644
index 0000000..dcda2c7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import com.android.systemui.R;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+public class TrustDrawable extends Drawable {
+
+ private static final long ENTERING_FROM_UNSET_START_DELAY = 200;
+ private static final long VISIBLE_DURATION = 1000;
+ private static final long EXIT_DURATION = 500;
+ private static final long ENTER_DURATION = 500;
+
+ private static final int ALPHA_VISIBLE_MIN = 0x26;
+ private static final int ALPHA_VISIBLE_MAX = 0x4c;
+
+ private static final int STATE_UNSET = -1;
+ private static final int STATE_GONE = 0;
+ private static final int STATE_ENTERING = 1;
+ private static final int STATE_VISIBLE = 2;
+ private static final int STATE_EXITING = 3;
+
+ private int mAlpha;
+ private boolean mAnimating;
+
+ private int mCurAlpha;
+ private float mCurInnerRadius;
+ private Animator mCurAnimator;
+ private int mState = STATE_UNSET;
+ private Paint mPaint;
+ private boolean mTrustManaged;
+
+ private final float mInnerRadiusVisibleMin;
+ private final float mInnerRadiusVisibleMax;
+ private final float mInnerRadiusExit;
+ private final float mInnerRadiusEnter;
+ private final float mThickness;
+
+ private final Animator mVisibleAnimator;
+
+ private final Interpolator mLinearOutSlowInInterpolator;
+ private final Interpolator mFastOutSlowInInterpolator;
+ private final Interpolator mAccelerateDecelerateInterpolator;
+
+ public TrustDrawable(Context context) {
+ Resources r = context.getResources();
+ mInnerRadiusVisibleMin = r.getDimension(R.dimen.trust_circle_inner_radius_visible_min);
+ mInnerRadiusVisibleMax = r.getDimension(R.dimen.trust_circle_inner_radius_visible_max);
+ mInnerRadiusExit = r.getDimension(R.dimen.trust_circle_inner_radius_exit);
+ mInnerRadiusEnter = r.getDimension(R.dimen.trust_circle_inner_radius_enter);
+ mThickness = r.getDimension(R.dimen.trust_circle_thickness);
+
+ mCurInnerRadius = mInnerRadiusEnter;
+
+ mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
+ context, android.R.interpolator.linear_out_slow_in);
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(
+ context, android.R.interpolator.fast_out_slow_in);
+ mAccelerateDecelerateInterpolator = new AccelerateDecelerateInterpolator();
+
+ mVisibleAnimator = makeVisibleAnimator();
+
+ mPaint = new Paint();
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setColor(Color.WHITE);
+ mPaint.setAntiAlias(true);
+ mPaint.setStrokeWidth(mThickness);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ int newAlpha = (mCurAlpha * mAlpha) / 256;
+ if (newAlpha == 0) {
+ return;
+ }
+ final Rect r = getBounds();
+ mPaint.setAlpha(newAlpha);
+ canvas.drawCircle(r.exactCenterX(), r.exactCenterY(), mCurInnerRadius, mPaint);
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ mAlpha = alpha;
+ }
+
+ @Override
+ public int getAlpha() {
+ return mAlpha;
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ public void start() {
+ if (!mAnimating) {
+ mAnimating = true;
+ updateState(true);
+ }
+ }
+
+ public void stop() {
+ if (mAnimating) {
+ mAnimating = false;
+ if (mCurAnimator != null) {
+ mCurAnimator.cancel();
+ mCurAnimator = null;
+ }
+ mState = STATE_UNSET;
+ mCurAlpha = 0;
+ mCurInnerRadius = mInnerRadiusEnter;
+ }
+ }
+
+ public void setTrustManaged(boolean trustManaged) {
+ if (trustManaged == mTrustManaged && mState != STATE_UNSET) return;
+ mTrustManaged = trustManaged;
+ if (mAnimating) {
+ updateState(true);
+ }
+ }
+
+ private void updateState(boolean animate) {
+ int nextState = mState;
+ if (mState == STATE_UNSET) {
+ nextState = mTrustManaged ? STATE_ENTERING : STATE_GONE;
+ } else if (mState == STATE_GONE) {
+ if (mTrustManaged) nextState = STATE_ENTERING;
+ } else if (mState == STATE_ENTERING) {
+ if (!mTrustManaged) nextState = STATE_EXITING;
+ } else if (mState == STATE_VISIBLE) {
+ if (!mTrustManaged) nextState = STATE_EXITING;
+ } else if (mState == STATE_EXITING) {
+ if (mTrustManaged) nextState = STATE_ENTERING;
+ }
+ if (!animate) {
+ if (nextState == STATE_ENTERING) nextState = STATE_VISIBLE;
+ if (nextState == STATE_EXITING) nextState = STATE_GONE;
+ }
+
+ if (nextState != mState) {
+ if (mCurAnimator != null) {
+ mCurAnimator.cancel();
+ mCurAnimator = null;
+ }
+
+ if (nextState == STATE_GONE) {
+ mCurAlpha = 0;
+ mCurInnerRadius = mInnerRadiusEnter;
+ } else if (nextState == STATE_ENTERING) {
+ mCurAnimator = makeEnterAnimator(mCurInnerRadius, mCurAlpha);
+ if (mState == STATE_UNSET) {
+ mCurAnimator.setStartDelay(ENTERING_FROM_UNSET_START_DELAY);
+ }
+ } else if (nextState == STATE_VISIBLE) {
+ mCurAlpha = ALPHA_VISIBLE_MAX;
+ mCurInnerRadius = mInnerRadiusVisibleMax;
+ mCurAnimator = mVisibleAnimator;
+ } else if (nextState == STATE_EXITING) {
+ mCurAnimator = makeExitAnimator(mCurInnerRadius, mCurAlpha);
+ }
+
+ mState = nextState;
+ if (mCurAnimator != null) {
+ mCurAnimator.start();
+ } else {
+ invalidateSelf();
+ }
+ }
+ }
+
+ private Animator makeVisibleAnimator() {
+ return makeAnimators(mInnerRadiusVisibleMax, mInnerRadiusVisibleMin,
+ ALPHA_VISIBLE_MAX, ALPHA_VISIBLE_MIN, VISIBLE_DURATION,
+ mAccelerateDecelerateInterpolator,
+ true /* repeating */, false /* stateUpdateListener */);
+ }
+
+ private Animator makeEnterAnimator(float radius, int alpha) {
+ return makeAnimators(radius, mInnerRadiusVisibleMax,
+ alpha, ALPHA_VISIBLE_MAX, ENTER_DURATION, mLinearOutSlowInInterpolator,
+ false /* repeating */, true /* stateUpdateListener */);
+ }
+
+ private Animator makeExitAnimator(float radius, int alpha) {
+ return makeAnimators(radius, mInnerRadiusExit,
+ alpha, 0, EXIT_DURATION, mFastOutSlowInInterpolator,
+ false /* repeating */, true /* stateUpdateListener */);
+ }
+
+ private Animator makeAnimators(float startRadius, float endRadius,
+ int startAlpha, int endAlpha, long duration, Interpolator interpolator,
+ boolean repeating, boolean stateUpdateListener) {
+ ValueAnimator alphaAnimator = configureAnimator(
+ ValueAnimator.ofInt(startAlpha, endAlpha),
+ duration, mAlphaUpdateListener, interpolator, repeating);
+ ValueAnimator sizeAnimator = configureAnimator(
+ ValueAnimator.ofFloat(startRadius, endRadius),
+ duration, mRadiusUpdateListener, interpolator, repeating);
+
+ AnimatorSet set = new AnimatorSet();
+ set.playTogether(alphaAnimator, sizeAnimator);
+ if (stateUpdateListener) {
+ set.addListener(new StateUpdateAnimatorListener());
+ }
+ return set;
+ }
+
+ private ValueAnimator configureAnimator(ValueAnimator animator, long duration,
+ ValueAnimator.AnimatorUpdateListener updateListener, Interpolator interpolator,
+ boolean repeating) {
+ animator.setDuration(duration);
+ animator.addUpdateListener(updateListener);
+ animator.setInterpolator(interpolator);
+ if (repeating) {
+ animator.setRepeatCount(ValueAnimator.INFINITE);
+ animator.setRepeatMode(ValueAnimator.REVERSE);
+ }
+ return animator;
+ }
+
+ private final ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener =
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mCurAlpha = (int) animation.getAnimatedValue();
+ invalidateSelf();
+ }
+ };
+
+ private final ValueAnimator.AnimatorUpdateListener mRadiusUpdateListener =
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mCurInnerRadius = (float) animation.getAnimatedValue();
+ invalidateSelf();
+ }
+ };
+
+ private class StateUpdateAnimatorListener extends AnimatorListenerAdapter {
+ boolean mCancelled;
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mCancelled = false;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mCancelled) {
+ updateState(false);
+ }
+ }
+ }
+}