Hint for phone and camera.
Bug: 15189049 Bug: 15126962 Change-Id: Ie28fc8202ace4af56542201d168572ef6ad78d19
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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] -->
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
Reference in New Issue
Block a user