Hint for phone and camera.

Bug: 15189049
Bug: 15126962
Change-Id: Ie28fc8202ace4af56542201d168572ef6ad78d19
This commit is contained in:
Jorim Jaggi
2014-06-02 19:29:39 +02:00
parent 282a615263
commit b3f0a2ff00
7 changed files with 197 additions and 27 deletions

View File

@@ -322,6 +322,13 @@
<!-- Distance between notifications and header when they are considered to be colliding. -->
<dimen name="header_notifications_collide_distance">24dp</dimen>
<!-- Move distance for the hint animations on the lockscreen (unlock, phone, camera)-->
<!-- Move distance for the unlock hint animation on the lockscreen -->
<dimen name="hint_move_distance">75dp</dimen>
<!-- Move distance for the other hint animations on the lockscreen (phone, camera)-->
<dimen name="hint_move_distance_sideways">60dp</dimen>
<!-- The width of the region on the left/right edge of the screen for performing the camera/
phone hints. -->
<dimen name="edge_tap_area_width">48dp</dimen>
</resources>

View File

@@ -572,6 +572,12 @@
<!-- Shows when people have pressed the unlock icon to explain how to unlock. [CHAR LIMIT=60] -->
<string name="keyguard_unlock">Swipe up to unlock</string>
<!-- Shows when people have clicked at the left edge of the screen to explain how to open the phone. In right-to-left languages, this is the opposite direction. [CHAR LIMIT=60] -->
<string name="phone_hint">Swipe right for phone</string>
<!-- Shows when people have clicked at the right edge of the screen to explain how to open the phone. In right-to-left languages, this is the opposite direction. [CHAR LIMIT=60] -->
<string name="camera_hint">Swipe left for camera</string>
<string name="bugreport_tile_extended" translatable="false">%s\n%s (%s)</string>
<!-- Zen mode condition: no exit criteria. [CHAR LIMIT=NONE] -->

View File

@@ -27,6 +27,7 @@ public class BounceInterpolator implements Interpolator {
@Override
public float getInterpolation(float t) {
t *= 11f / 10f;
if (t < 4f / 11f) {
return SCALE_FACTOR * t * t;
} else if (t < 8f / 11f) {
@@ -36,8 +37,7 @@ public class BounceInterpolator implements Interpolator {
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;
return 1;
}
}
}

View File

@@ -28,6 +28,7 @@ import android.view.ViewConfiguration;
import android.view.ViewPropertyAnimator;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import com.android.systemui.R;
import com.android.systemui.statusbar.FlingAnimationUtils;
@@ -40,6 +41,9 @@ public class KeyguardPageSwipeHelper {
private static final float SWIPE_MAX_ICON_SCALE_AMOUNT = 2.0f;
private static final float SWIPE_RESTING_ALPHA_AMOUNT = 0.7f;
private static final long HINT_PHASE1_DURATION = 250;
private static final long HINT_PHASE2_DURATION = 450;
private final Context mContext;
private FlingAnimationUtils mFlingAnimationUtils;
@@ -54,11 +58,13 @@ public class KeyguardPageSwipeHelper {
private int mTouchSlop;
private int mMinTranslationAmount;
private int mMinFlingVelocity;
private int mHintDistance;
private PowerManager mPowerManager;
private final View mLeftIcon;
private final View mCenterIcon;
private final View mRightIcon;
private Interpolator mFastOutSlowIn;
private Interpolator mBounceInterpolator;
private Animator mSwipeAnimator;
private boolean mCallbackCalled;
@@ -81,9 +87,12 @@ public class KeyguardPageSwipeHelper {
mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
mMinTranslationAmount = mContext.getResources().getDimensionPixelSize(
R.dimen.keyguard_min_swipe_amount);
mHintDistance =
mContext.getResources().getDimensionPixelSize(R.dimen.hint_move_distance_sideways);
mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.4f);
mFastOutSlowIn = AnimationUtils.loadInterpolator(mContext,
android.R.interpolator.fast_out_slow_in);
mBounceInterpolator = new BounceInterpolator();
}
public boolean onTouchEvent(MotionEvent event) {
@@ -168,6 +177,83 @@ public class KeyguardPageSwipeHelper {
return false;
}
public void startHintAnimation(boolean right, Runnable onFinishedListener) {
startHintAnimationPhase1(right, onFinishedListener);
}
/**
* Phase 1: Move everything sidewards.
*/
private void startHintAnimationPhase1(boolean right, final Runnable onFinishedListener) {
float target = right ? -mHintDistance : mHintDistance;
startHintTranslationAnimations(target, HINT_PHASE1_DURATION, mFastOutSlowIn);
ValueAnimator animator = ValueAnimator.ofFloat(mTranslation, target);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mTranslation = (float) animation.getAnimatedValue();
}
});
animator.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled;
@Override
public void onAnimationCancel(Animator animation) {
mCancelled = true;
}
@Override
public void onAnimationEnd(Animator animation) {
if (mCancelled) {
mSwipeAnimator = null;
onFinishedListener.run();
} else {
startUnlockHintAnimationPhase2(onFinishedListener);
}
}
});
animator.setInterpolator(mFastOutSlowIn);
animator.setDuration(HINT_PHASE1_DURATION);
animator.start();
mSwipeAnimator = animator;
}
/**
* Phase 2: Move back.
*/
private void startUnlockHintAnimationPhase2(final Runnable onFinishedListener) {
startHintTranslationAnimations(0f /* target */, HINT_PHASE2_DURATION, mBounceInterpolator);
ValueAnimator animator = ValueAnimator.ofFloat(mTranslation, 0f);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mTranslation = (float) animation.getAnimatedValue();
}
});
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mSwipeAnimator = null;
onFinishedListener.run();
}
});
animator.setInterpolator(mBounceInterpolator);
animator.setDuration(HINT_PHASE2_DURATION);
animator.start();
mSwipeAnimator = animator;
}
private void startHintTranslationAnimations(float target, long duration,
Interpolator interpolator) {
ArrayList<View> targetViews = mCallback.getTranslationViews();
for (View targetView : targetViews) {
targetView.animate()
.setDuration(duration)
.setInterpolator(interpolator)
.translationX(target);
}
}
private void onUserActivity(long when) {
mPowerManager.userActivity(when, false);
}
@@ -180,7 +266,6 @@ public class KeyguardPageSwipeHelper {
View targetView = mTranslation > 0 ? mLeftIcon : mRightIcon;
targetView.animate().cancel();
if (mSwipeAnimator != null) {
mSwipeAnimator.removeAllListeners();
mSwipeAnimator.cancel();
hideInactiveIcons(true);
}
@@ -218,11 +303,18 @@ public class KeyguardPageSwipeHelper {
}
});
animator.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled;
@Override
public void onAnimationCancel(Animator animation) {
mCancelled = true;
}
@Override
public void onAnimationEnd(Animator animation) {
mSwipeAnimator = null;
mSwipingInProgress = false;
if (!snapBack && !mCallbackCalled) {
if (!snapBack && !mCallbackCalled && !mCancelled) {
// ensure that the callback is called eventually
mCallback.onAnimationToSideStarted(mTranslation < 0);

View File

@@ -422,7 +422,9 @@ public class NotificationPanelView extends PanelView implements
}
// TODO: Handle doublefinger swipe to notifications again. Look at history for a reference
// implementation.
if (!mIsExpanding && !mQsExpanded && mStatusBar.getBarState() != StatusBarState.SHADE) {
if ((!mIsExpanding || mHintAnimationRunning)
&& !mQsExpanded
&& mStatusBar.getBarState() != StatusBarState.SHADE) {
mPageSwiper.onTouchEvent(event);
if (mPageSwiper.isSwipingInProgress()) {
return true;
@@ -796,8 +798,7 @@ public class NotificationPanelView extends PanelView implements
protected void onTrackingStopped(boolean expand) {
super.onTrackingStopped(expand);
mOverExpansion = 0.0f;
mNotificationStackScroller.setOverScrolledPixels(0.0f, true /* onTop */,
true /* animate */);
mNotificationStackScroller.setOverScrolledPixels(0.0f, true /* onTop */, true /* animate */);
if (expand && (mStatusBar.getBarState() == StatusBarState.KEYGUARD
|| mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED)) {
mPageSwiper.showAllIcons(true);
@@ -837,14 +838,36 @@ public class NotificationPanelView extends PanelView implements
@Override
public void onAnimationToSideStarted(boolean rightPage) {
if (rightPage) {
mKeyguardBottomArea.launchCamera();
} else {
boolean start = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? rightPage : !rightPage;
if (start) {
mKeyguardBottomArea.launchPhone();
} else {
mKeyguardBottomArea.launchCamera();
}
mBlockTouches = true;
}
@Override
protected void onEdgeClicked(boolean right) {
if ((right && getRightIcon().getVisibility() != View.VISIBLE)
|| (!right && getLeftIcon().getVisibility() != View.VISIBLE)) {
return;
}
mHintAnimationRunning = true;
mPageSwiper.startHintAnimation(right, new Runnable() {
@Override
public void run() {
mHintAnimationRunning = false;
mStatusBar.onHintFinished();
}
});
boolean start = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? right : !right;
if (start) {
mStatusBar.onPhoneHintStarted();
} else {
mStatusBar.onCameraHintStarted();
}
}
@Override
public float getPageWidth() {
@@ -858,7 +881,9 @@ public class NotificationPanelView extends PanelView implements
@Override
public View getLeftIcon() {
return mKeyguardBottomArea.getPhoneImageView();
return getLayoutDirection() == LAYOUT_DIRECTION_RTL
? mKeyguardBottomArea.getCameraImageView()
: mKeyguardBottomArea.getPhoneImageView();
}
@Override
@@ -868,6 +893,8 @@ public class NotificationPanelView extends PanelView implements
@Override
public View getRightIcon() {
return mKeyguardBottomArea.getCameraImageView();
return getLayoutDirection() == LAYOUT_DIRECTION_RTL
? mKeyguardBottomArea.getPhoneImageView()
: mKeyguardBottomArea.getCameraImageView();
}
}

View File

@@ -50,6 +50,7 @@ public abstract class PanelView extends FrameLayout {
protected PhoneStatusBar mStatusBar;
private float mPeekHeight;
private float mHintDistance;
private int mEdgeTapAreaWidth;
private float mInitialOffsetOnTouch;
private float mExpandedFraction = 0;
private float mExpandedHeight = 0;
@@ -59,6 +60,7 @@ public abstract class PanelView extends FrameLayout {
private boolean mTouchSlopExceeded;
private int mTrackingPointer;
protected int mTouchSlop;
protected boolean mHintAnimationRunning;
private ValueAnimator mHeightAnimator;
private ObjectAnimator mPeekAnimator;
@@ -111,6 +113,7 @@ public abstract class PanelView extends FrameLayout {
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
mHintDistance = res.getDimension(R.dimen.hint_move_distance);
mEdgeTapAreaWidth = res.getDimensionPixelSize(R.dimen.edge_tap_area_width);
}
private void trackMovement(MotionEvent event) {
@@ -147,7 +150,6 @@ public abstract class PanelView extends FrameLayout {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mInitialTouchY = y;
mInitialTouchX = x;
mInitialOffsetOnTouch = mExpandedHeight;
@@ -156,10 +158,11 @@ public abstract class PanelView extends FrameLayout {
initVelocityTracker();
}
trackMovement(event);
if (!waitForTouchSlop || mHeightAnimator != null) {
if (!waitForTouchSlop || (mHeightAnimator != null && !mHintAnimationRunning)) {
if (mHeightAnimator != null) {
mHeightAnimator.cancel(); // end any outstanding animations
}
mTouchSlopExceeded = true;
onTrackingStarted();
}
if (mExpandedHeight == 0) {
@@ -222,7 +225,7 @@ public abstract class PanelView extends FrameLayout {
onTrackingStopped(expand);
fling(vel, expand);
} else {
boolean expands = onEmptySpaceClick();
boolean expands = onEmptySpaceClick(mInitialTouchX);
onTrackingStopped(expands);
}
if (mVelocityTracker != null) {
@@ -279,8 +282,9 @@ public abstract class PanelView extends FrameLayout {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
if (mHeightAnimator != null) {
if (mHeightAnimator != null && !mHintAnimationRunning) {
mHeightAnimator.cancel(); // end any outstanding animations
mTouchSlopExceeded = true;
return true;
}
mInitialTouchY = y;
@@ -305,6 +309,9 @@ public abstract class PanelView extends FrameLayout {
trackMovement(event);
if (scrolledToBottom) {
if (h < -mTouchSlop && h < -Math.abs(x - mInitialTouchX)) {
if (mHeightAnimator != null) {
mHeightAnimator.cancel();
}
mInitialOffsetOnTouch = mExpandedHeight;
mInitialTouchY = y;
mInitialTouchX = x;
@@ -550,14 +557,22 @@ public abstract class PanelView extends FrameLayout {
}
cancelPeek();
onExpandingStarted();
startUnlockHintAnimationPhase1();
startUnlockHintAnimationPhase1(new Runnable() {
@Override
public void run() {
onExpandingFinished();
mStatusBar.onHintFinished();
mHintAnimationRunning = false;
}
});
mStatusBar.onUnlockHintStarted();
mHintAnimationRunning = true;
}
/**
* Phase 1: Move everything upwards.
*/
private void startUnlockHintAnimationPhase1() {
private void startUnlockHintAnimationPhase1(final Runnable onAnimationFinished) {
float target = Math.max(0, getMaxPanelHeight() - mHintDistance);
ValueAnimator animator = createHeightAnimator(target);
animator.setDuration(250);
@@ -574,10 +589,9 @@ public abstract class PanelView extends FrameLayout {
public void onAnimationEnd(Animator animation) {
if (mCancelled) {
mHeightAnimator = null;
onExpandingFinished();
mStatusBar.onUnlockHintFinished();
onAnimationFinished.run();
} else {
startUnlockHintAnimationPhase2();
startUnlockHintAnimationPhase2(onAnimationFinished);
}
}
});
@@ -588,7 +602,7 @@ public abstract class PanelView extends FrameLayout {
/**
* Phase 2: Bounce down.
*/
private void startUnlockHintAnimationPhase2() {
private void startUnlockHintAnimationPhase2(final Runnable onAnimationFinished) {
ValueAnimator animator = createHeightAnimator(getMaxPanelHeight());
animator.setDuration(450);
animator.setInterpolator(mBounceInterpolator);
@@ -596,8 +610,7 @@ public abstract class PanelView extends FrameLayout {
@Override
public void onAnimationEnd(Animator animation) {
mHeightAnimator = null;
onExpandingFinished();
mStatusBar.onUnlockHintFinished();
onAnimationFinished.run();
}
});
animator.start();
@@ -620,7 +633,22 @@ public abstract class PanelView extends FrameLayout {
*
* @return whether the panel will be expanded after the action performed by this method
*/
private boolean onEmptySpaceClick() {
private boolean onEmptySpaceClick(float x) {
if (mHintAnimationRunning) {
return true;
}
if (x < mEdgeTapAreaWidth) {
onEdgeClicked(false /* right */);
return true;
} else if (x > getWidth() - mEdgeTapAreaWidth) {
onEdgeClicked(true /* right */);
return true;
} else {
return onMiddleClicked();
}
}
private boolean onMiddleClicked() {
switch (mStatusBar.getBarState()) {
case StatusBarState.KEYGUARD:
startUnlockHintAnimation();
@@ -636,6 +664,8 @@ public abstract class PanelView extends FrameLayout {
}
}
protected abstract void onEdgeClicked(boolean right);
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"

View File

@@ -2963,10 +2963,18 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mKeyguardIndicationTextView.switchIndication(R.string.keyguard_unlock);
}
public void onUnlockHintFinished() {
public void onHintFinished() {
mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
}
public void onCameraHintStarted() {
mKeyguardIndicationTextView.switchIndication(R.string.camera_hint);
}
public void onPhoneHintStarted() {
mKeyguardIndicationTextView.switchIndication(R.string.phone_hint);
}
public void onTrackingStopped(boolean expand) {
if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
if (!expand && !mUnlockMethodCache.isMethodInsecure()) {