diff options
author | Adrian Roos <roosa@google.com> | 2014-08-19 22:23:37 +0200 |
---|---|---|
committer | Adrian Roos <roosa@google.com> | 2014-08-20 18:54:25 +0200 |
commit | 2e3ccbb37e21eb40308f83fb185209773147f78d (patch) | |
tree | f6878d92c5d31eb9156ffd0e88bad5b6587b0feb /packages/SystemUI | |
parent | 03861d072131563561bb5873d35dc346e82bd904 (diff) | |
download | frameworks_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')
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); + } + } + } +} |