Hint for phone and camera.
Bug: 15189049
Bug: 15126962
Change-Id: Ie28fc8202ace4af56542201d168572ef6ad78d19
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 60a5643..c68388e 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 373f11f..7ab010a 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 367d326..0fdc185 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 @@
@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 @@
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 0e3e9e6..25bb41a 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.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 @@
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 @@
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 @@
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 @@
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 @@
View targetView = mTranslation > 0 ? mLeftIcon : mRightIcon;
targetView.animate().cancel();
if (mSwipeAnimator != null) {
- mSwipeAnimator.removeAllListeners();
mSwipeAnimator.cancel();
hideInactiveIcons(true);
}
@@ -218,11 +303,18 @@
}
});
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 7fc7cdb..252f153 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 @@
}
// 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 @@
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 @@
@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 @@
@Override
public View getLeftIcon() {
- return mKeyguardBottomArea.getPhoneImageView();
+ return getLayoutDirection() == LAYOUT_DIRECTION_RTL
+ ? mKeyguardBottomArea.getCameraImageView()
+ : mKeyguardBottomArea.getPhoneImageView();
}
@Override
@@ -868,6 +893,8 @@
@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 4686933..ea8dd68 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 @@
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 @@
private boolean mTouchSlopExceeded;
private int mTrackingPointer;
protected int mTouchSlop;
+ protected boolean mHintAnimationRunning;
private ValueAnimator mHeightAnimator;
private ObjectAnimator mPeekAnimator;
@@ -111,6 +113,7 @@
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 @@
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
-
mInitialTouchY = y;
mInitialTouchX = x;
mInitialOffsetOnTouch = mExpandedHeight;
@@ -156,10 +158,11 @@
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 @@
onTrackingStopped(expand);
fling(vel, expand);
} else {
- boolean expands = onEmptySpaceClick();
+ boolean expands = onEmptySpaceClick(mInitialTouchX);
onTrackingStopped(expands);
}
if (mVelocityTracker != null) {
@@ -279,8 +282,9 @@
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 @@
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 @@
}
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 void onAnimationEnd(Animator animation) {
if (mCancelled) {
mHeightAnimator = null;
- onExpandingFinished();
- mStatusBar.onUnlockHintFinished();
+ onAnimationFinished.run();
} else {
- startUnlockHintAnimationPhase2();
+ startUnlockHintAnimationPhase2(onAnimationFinished);
}
}
});
@@ -588,7 +602,7 @@
/**
* 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 @@
@Override
public void onAnimationEnd(Animator animation) {
mHeightAnimator = null;
- onExpandingFinished();
- mStatusBar.onUnlockHintFinished();
+ onAnimationFinished.run();
}
});
animator.start();
@@ -620,7 +633,22 @@
*
* @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 @@
}
}
+ 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 b1216e69..bb0785c 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 @@
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()) {