Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fd3b324f authored by Adrian Roos's avatar Adrian Roos Committed by Android (Google) Code Review
Browse files

Merge "Implement trust managed visuals" into lmp-dev

parents 52097a22 2e3ccbb3
Loading
Loading
Loading
Loading
+0 −22
Original line number Original line Diff line number Diff line
<?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
+12 −0
Original line number Original line Diff line number Diff line
@@ -430,4 +430,16 @@


    <!-- The text size for battery level -->
    <!-- The text size for battery level -->
    <dimen name="battery_level_text_size">12sp</dimen>
    <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>
</resources>
+38 −6
Original line number Original line Diff line number Diff line
@@ -82,21 +82,24 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
    private KeyguardIndicationController mIndicationController;
    private KeyguardIndicationController mIndicationController;
    private boolean mFaceUnlockRunning;
    private boolean mFaceUnlockRunning;


    private final TrustDrawable mTrustDrawable;

    public KeyguardBottomAreaView(Context context) {
    public KeyguardBottomAreaView(Context context) {
        super(context);
        this(context, null);
    }
    }


    public KeyguardBottomAreaView(Context context, AttributeSet attrs) {
    public KeyguardBottomAreaView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this(context, attrs, 0);
    }
    }


    public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr) {
    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,
    public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr,
            int defStyleRes) {
            int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        super(context, attrs, defStyleAttr, defStyleRes);
        mTrustDrawable = new TrustDrawable(mContext);
    }
    }


    @Override
    @Override
@@ -120,6 +123,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
        mPreviewInflater = new PreviewInflater(mContext, new LockPatternUtils(mContext));
        mPreviewInflater = new PreviewInflater(mContext, new LockPatternUtils(mContext));
        inflatePreviews();
        inflatePreviews();
        mLockIcon.setOnClickListener(this);
        mLockIcon.setOnClickListener(this);
        mLockIcon.setBackground(mTrustDrawable);
    }
    }


    @Override
    @Override
@@ -267,14 +271,31 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
    @Override
    @Override
    protected void onVisibilityChanged(View changedView, int visibility) {
    protected void onVisibilityChanged(View changedView, int visibility) {
        super.onVisibilityChanged(changedView, visibility);
        super.onVisibilityChanged(changedView, visibility);
        if (isShown()) {
            mTrustDrawable.start();
        } else {
            mTrustDrawable.stop();
        }
        if (changedView == this && visibility == VISIBLE) {
        if (changedView == this && visibility == VISIBLE) {
            updateLockIcon();
            updateLockIcon();
            updateCameraVisibility();
            updateCameraVisibility();
        }
        }
    }
    }


    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mTrustDrawable.stop();
    }

    private void updateLockIcon() {
    private void updateLockIcon() {
        if (getVisibility() != VISIBLE) {
        boolean visible = isShown() && KeyguardUpdateMonitor.getInstance(mContext).isScreenOn();
        if (visible) {
            mTrustDrawable.start();
        } else {
            mTrustDrawable.stop();
        }
        if (!visible) {
            return;
            return;
        }
        }
        // TODO: Real icon for facelock.
        // TODO: Real icon for facelock.
@@ -283,11 +304,12 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
                : R.drawable.ic_lock_24dp;
                : R.drawable.ic_lock_24dp;
        mLockIcon.setImageResource(iconRes);
        mLockIcon.setImageResource(iconRes);
        boolean trustManaged = mUnlockMethodCache.isTrustManaged();
        boolean trustManaged = mUnlockMethodCache.isTrustManaged();
        mLockIcon.setBackgroundResource(trustManaged && !mFaceUnlockRunning
        mTrustDrawable.setTrustManaged(trustManaged);
                ? R.drawable.trust_circle : 0);
        mLockIcon.setClickable(trustManaged);
        mLockIcon.setClickable(trustManaged);
    }
    }




    public KeyguardAffordanceView getPhoneView() {
    public KeyguardAffordanceView getPhoneView() {
        return mPhoneImageView;
        return mPhoneImageView;
    }
    }
@@ -359,6 +381,16 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
            mFaceUnlockRunning = running;
            mFaceUnlockRunning = running;
            updateLockIcon();
            updateLockIcon();
        }
        }

        @Override
        public void onScreenTurnedOn() {
            updateLockIcon();
        }

        @Override
        public void onScreenTurnedOff(int why) {
            updateLockIcon();
        }
    };
    };


    public void setKeyguardIndicationController(
    public void setKeyguardIndicationController(
+297 −0
Original line number Original line Diff line number Diff line
/*
 * 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);
            }
        }
    }
}