diff options
7 files changed, 197 insertions, 27 deletions
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 60a5643ba35d..c68388e85da8 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -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> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 373f11f532c0..7ab010a6b2e0 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -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] --> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BounceInterpolator.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BounceInterpolator.java index 367d32611dbb..0fdc185ec648 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BounceInterpolator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BounceInterpolator.java @@ -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; } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java index 0e3e9e6ea43a..25bb41a73223 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java @@ -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); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 7fc7cdb8221c..252f1539ba12 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -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(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index 4686933be6da..ea8dd685e6ec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -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" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index b1216e69d0b4..bb0785cec326 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -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()) { |