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

Commit 90129587 authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

Implement unlock hint.

Before, users didn't have any clue how to unlock their phone. Now
they have. Also, a tap now dismisses the notification shade in the
unlocked state.

Bug: 14080971
Bug: 15189435
Bug: 15282191
Bug: 15126962

Change-Id: I779344b043b4415809dd98217b7cb9ff6d57fa3e
parent 290600a0
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -318,4 +318,7 @@

    <!-- Volume panel z depth -->
    <dimen name="volume_panel_z">3dp</dimen>

    <!-- Move distance for the hint animations on the lockscreen (unlock, phone, camera)-->
    <dimen name="hint_move_distance">75dp</dimen>
</resources>
+43 −0
Original line number 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 android.view.animation.Interpolator;

/**
 * An implementation of a bouncer interpolator optimized for unlock hinting.
 */
public class BounceInterpolator implements Interpolator {

    private final static float SCALE_FACTOR = 7.5625f;

    @Override
    public float getInterpolation(float t) {
        if (t < 4f / 11f) {
            return SCALE_FACTOR * t * t;
        } else if (t < 8f / 11f) {
            float t2 = t - 6f / 11f;
            return SCALE_FACTOR * t2 * t2 + 3f / 4f;
        } else if (t < 10f / 11f) {
            float t2 = t - 9f / 11f;
            return SCALE_FACTOR * t2 * t2 + 15f / 16f;
        } else {
            float t2 = t - 21f / 22f;
            return SCALE_FACTOR * t2 * t2 + 63f / 64f;
        }
    }
}
+0 −1
Original line number Diff line number Diff line
@@ -51,7 +51,6 @@ public class NotificationPanelView extends PanelView implements
    private static float EXPANSION_RUBBER_BAND_EXTRA_FACTOR = 0.6f;

    private KeyguardPageSwipeHelper mPageSwiper;
    PhoneStatusBar mStatusBar;
    private StatusBarHeaderView mHeader;
    private View mQsContainer;
    private View mQsPanel;
+133 −23
Original line number Diff line number Diff line
@@ -27,10 +27,13 @@ import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;

import com.android.systemui.R;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.StatusBarState;

import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -44,13 +47,16 @@ public abstract class PanelView extends FrameLayout {
        Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
    }

    protected PhoneStatusBar mStatusBar;
    private float mPeekHeight;
    private float mHintDistance;
    private float mInitialOffsetOnTouch;
    private float mExpandedFraction = 0;
    private float mExpandedHeight = 0;
    private boolean mJustPeeked;
    private boolean mClosing;
    private boolean mTracking;
    private boolean mTouchSlopExceeded;
    private int mTrackingPointer;
    protected int mTouchSlop;

@@ -66,6 +72,9 @@ public abstract class PanelView extends FrameLayout {
    private float mInitialTouchY;
    private float mInitialTouchX;

    private Interpolator mLinearOutSlowInInterpolator;
    private Interpolator mBounceInterpolator;

    protected void onExpandingFinished() {
        mBar.onExpandingFinished();
    }
@@ -89,6 +98,9 @@ public abstract class PanelView extends FrameLayout {
    public PanelView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mFlingAnimationUtils = new FlingAnimationUtils(context, 0.6f);
        mLinearOutSlowInInterpolator =
                AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
        mBounceInterpolator = new BounceInterpolator();
    }

    protected void loadDimens() {
@@ -98,6 +110,7 @@ public abstract class PanelView extends FrameLayout {

        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
        mTouchSlop = configuration.getScaledTouchSlop();
        mHintDistance = res.getDimension(R.dimen.hint_move_distance);
    }

    private void trackMovement(MotionEvent event) {
@@ -138,6 +151,7 @@ public abstract class PanelView extends FrameLayout {
                mInitialTouchY = y;
                mInitialTouchX = x;
                mInitialOffsetOnTouch = mExpandedHeight;
                mTouchSlopExceeded = false;
                if (mVelocityTracker == null) {
                    initVelocityTracker();
                }
@@ -170,8 +184,9 @@ public abstract class PanelView extends FrameLayout {

            case MotionEvent.ACTION_MOVE:
                float h = y - mInitialTouchY;
                if (waitForTouchSlop && !mTracking && Math.abs(h) > mTouchSlop
                        && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
                if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
                    mTouchSlopExceeded = true;
                    if (waitForTouchSlop && !mTracking) {
                        mInitialOffsetOnTouch = mExpandedHeight;
                        mInitialTouchX = x;
                        mInitialTouchY = y;
@@ -181,6 +196,7 @@ public abstract class PanelView extends FrameLayout {
                        onTrackingStarted();
                        h = 0;
                    }
                }
                final float newHeight = h + mInitialOffsetOnTouch;
                if (newHeight > mPeekHeight) {
                    if (mPeekAnimator != null && mPeekAnimator.isStarted()) {
@@ -200,10 +216,15 @@ public abstract class PanelView extends FrameLayout {
            case MotionEvent.ACTION_CANCEL:
                mTrackingPointer = -1;
                trackMovement(event);
                if (mTracking && mTouchSlopExceeded) {
                    float vel = getCurrentVelocity();
                    boolean expand = flingExpands(vel);
                    onTrackingStopped(expand);
                    fling(vel, expand);
                } else {
                    boolean expands = onEmptySpaceClick();
                    onTrackingStopped(expands);
                }
                if (mVelocityTracker != null) {
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
@@ -264,6 +285,7 @@ public abstract class PanelView extends FrameLayout {
                }
                mInitialTouchY = y;
                mInitialTouchX = x;
                mTouchSlopExceeded = false;
                initVelocityTracker();
                trackMovement(event);
                break;
@@ -287,6 +309,7 @@ public abstract class PanelView extends FrameLayout {
                        mInitialTouchY = y;
                        mInitialTouchX = x;
                        mTracking = true;
                        mTouchSlopExceeded = true;
                        onTrackingStarted();
                        return true;
                    }
@@ -344,7 +367,7 @@ public abstract class PanelView extends FrameLayout {
            mBar.panelExpansionChanged(this, mExpandedFraction);
            return;
        }
        ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, target);
        ValueAnimator animator = createHeightAnimator(target);
        if (expand) {
            mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, getHeight());
        } else {
@@ -356,12 +379,6 @@ public abstract class PanelView extends FrameLayout {
                animator.setDuration((long) (animator.getDuration() / 1.75f));
            }
        }
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                setExpandedHeight((Float) animation.getAnimatedValue());
            }
        });
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
@@ -449,9 +466,7 @@ public abstract class PanelView extends FrameLayout {
        mOverExpansion = overExpansion;
    }

    protected void onHeightUpdated(float expandedHeight) {
        requestLayout();
    }
    protected abstract void onHeightUpdated(float expandedHeight);

    /**
     * This returns the maximum height of the panel. Children should override this if their
@@ -526,6 +541,101 @@ public abstract class PanelView extends FrameLayout {
        }
    }

    protected void startUnlockHintAnimation() {

        // We don't need to hint the user if an animation is already running or the user is changing
        // the expansion.
        if (mHeightAnimator != null || mTracking) {
            return;
        }
        cancelPeek();
        onExpandingStarted();
        startUnlockHintAnimationPhase1();
        mStatusBar.onUnlockHintStarted();
    }

    /**
     * Phase 1: Move everything upwards.
     */
    private void startUnlockHintAnimationPhase1() {
        float target = Math.max(0, getMaxPanelHeight() - mHintDistance);
        ValueAnimator animator = createHeightAnimator(target);
        animator.setDuration(250);
        animator.setInterpolator(mLinearOutSlowInInterpolator);
        animator.addListener(new AnimatorListenerAdapter() {
            private boolean mCancelled;

            @Override
            public void onAnimationCancel(Animator animation) {
                mCancelled = true;
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                if (mCancelled) {
                    mHeightAnimator = null;
                    onExpandingFinished();
                    mStatusBar.onUnlockHintFinished();
                } else {
                    startUnlockHintAnimationPhase2();
                }
            }
        });
        animator.start();
        mHeightAnimator = animator;
    }

    /**
     * Phase 2: Bounce down.
     */
    private void startUnlockHintAnimationPhase2() {
        ValueAnimator animator = createHeightAnimator(getMaxPanelHeight());
        animator.setDuration(450);
        animator.setInterpolator(mBounceInterpolator);
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                mHeightAnimator = null;
                onExpandingFinished();
                mStatusBar.onUnlockHintFinished();
            }
        });
        animator.start();
        mHeightAnimator = animator;
    }

    private ValueAnimator createHeightAnimator(float targetHeight) {
        ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, targetHeight);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                setExpandedHeight((Float) animation.getAnimatedValue());
            }
        });
        return animator;
    }

    /**
     * Gets called when the user performs a click anywhere in the empty area of the panel.
     *
     * @return whether the panel will be expanded after the action performed by this method
     */
    private boolean onEmptySpaceClick() {
        switch (mStatusBar.getBarState()) {
            case StatusBarState.KEYGUARD:
                startUnlockHintAnimation();
                return true;
            case StatusBarState.SHADE_LOCKED:
                // TODO: Go to Keyguard again.
                return true;
            case StatusBarState.SHADE:
                collapse();
                return false;
            default:
                return true;
        }
    }

    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%d closing=%s"
                + " tracking=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s"
+8 −6
Original line number Diff line number Diff line
@@ -2957,15 +2957,17 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
    }

    public void onTrackingStarted() {
        if (mState == StatusBarState.KEYGUARD) {
            mKeyguardIndicationTextView.switchIndication(R.string.keyguard_unlock);
    }

    public void onUnlockHintStarted() {
        mKeyguardIndicationTextView.switchIndication(R.string.keyguard_unlock);
    }

    public void onTrackingStopped(boolean expand) {
        if (mState == StatusBarState.KEYGUARD) {
    public void onUnlockHintFinished() {
        mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
    }

    public void onTrackingStopped(boolean expand) {
        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
            if (!expand && !mUnlockMethodCache.isMethodInsecure()) {
                showBouncer();
Loading