diff options
11 files changed, 885 insertions, 312 deletions
diff --git a/packages/SystemUI/res/drawable/ic_chevron_left.xml b/packages/SystemUI/res/drawable/ic_chevron_left.xml new file mode 100644 index 0000000..27c2034 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_chevron_left.xml @@ -0,0 +1,28 @@ +<!-- + ~ 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 + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="24dp" + android:height="24dp"/> + + <viewport + android:viewportWidth="36.0" + android:viewportHeight="36.0"/> + + <path + android:fill="#ffffffff" + android:pathData="M23.1,11.1l-2.1000004,-2.1000004 -9.0,9.0 9.0,9.0 2.1000004,-2.1000004 -6.8999996,-6.8999996z"/> +</vector> diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml index db5983b..42fb740 100644 --- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml +++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml @@ -22,7 +22,7 @@ android:layout_height="match_parent" android:layout_width="match_parent" > - <com.android.systemui.statusbar.AlphaImageView + <com.android.systemui.statusbar.KeyguardAffordanceView android:id="@+id/camera_button" android:layout_height="64dp" android:layout_width="64dp" @@ -32,7 +32,7 @@ android:scaleType="center" android:contentDescription="@string/accessibility_camera_button" /> - <com.android.systemui.statusbar.AlphaImageView + <com.android.systemui.statusbar.KeyguardAffordanceView android:id="@+id/phone_button" android:layout_height="64dp" android:layout_width="64dp" @@ -52,7 +52,7 @@ android:textColor="#ffffff" android:textAppearance="?android:attr/textAppearanceSmall"/> - <com.android.systemui.statusbar.AlphaImageView + <com.android.systemui.statusbar.KeyguardAffordanceView android:id="@+id/lock_icon" android:layout_width="64dp" android:layout_height="64dp" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 9bc2b0d..4e536e4 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -295,6 +295,15 @@ <!-- The minimum amount the user needs to swipe to go to the camera / phone. --> <dimen name="keyguard_min_swipe_amount">75dp</dimen> + <!-- The minimum background radius when swiping to a side for the camera / phone affordances. --> + <dimen name="keyguard_affordance_min_background_radius">30dp</dimen> + + <!-- The grow amount for the camera and phone circles when hinting --> + <dimen name="hint_grow_amount_sideways">60dp</dimen> + + <!-- The chevron padding to the circle when hinting --> + <dimen name="hint_chevron_circle_padding">16dp</dimen> + <!-- Volume panel dialog y offset --> <dimen name="volume_panel_top">0dp</dimen> @@ -317,9 +326,6 @@ <!-- 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> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 0a288d9..225b6af 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -244,9 +244,6 @@ public abstract class BaseStatusBar extends SystemUI implements // the user switches to home. We know it is safe to do at this // point, so make sure new activity switches are now allowed. ActivityManagerNative.getDefault().resumeAppSwitches(); - // Also, notifications can be launched from the lock screen, - // so dismiss the lock screen when the activity starts. - ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); } catch (RemoteException e) { } @@ -1153,9 +1150,6 @@ public abstract class BaseStatusBar extends SystemUI implements // the user switches to home. We know it is safe to do at this // point, so make sure new activity switches are now allowed. ActivityManagerNative.getDefault().resumeAppSwitches(); - // Also, notifications can be launched from the lock screen, - // so dismiss the lock screen when the activity starts. - ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); } catch (RemoteException e) { } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java new file mode 100644 index 0000000..845e0ae --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java @@ -0,0 +1,429 @@ +/* + * 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; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ArgbEvaluator; +import android.animation.PropertyValuesHolder; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; +import android.widget.ImageView; +import com.android.systemui.R; + +/** + * An ImageView which does not have overlapping renderings commands and therefore does not need a + * layer when alpha is changed. + */ +public class KeyguardAffordanceView extends ImageView { + + private static final long CIRCLE_APPEAR_DURATION = 80; + private static final long CIRCLE_DISAPPEAR_MAX_DURATION = 200; + private static final long NORMAL_ANIMATION_DURATION = 200; + public static final float MAX_ICON_SCALE_AMOUNT = 1.5f; + public static final float MIN_ICON_SCALE_AMOUNT = 0.8f; + + private final int mMinBackgroundRadius; + private final Paint mCirclePaint; + private final Interpolator mAppearInterpolator; + private final Interpolator mDisappearInterpolator; + private final int mInverseColor; + private final int mNormalColor; + private final ArgbEvaluator mColorInterpolator; + private final FlingAnimationUtils mFlingAnimationUtils; + private final Drawable mArrowDrawable; + private final int mHintChevronPadding; + private float mCircleRadius; + private int mCenterX; + private int mCenterY; + private ValueAnimator mCircleAnimator; + private ValueAnimator mAlphaAnimator; + private ValueAnimator mScaleAnimator; + private ValueAnimator mArrowAnimator; + private float mCircleStartValue; + private boolean mCircleWillBeHidden; + private int[] mTempPoint = new int[2]; + private float mImageScale; + private int mCircleColor; + private boolean mIsLeft; + private float mArrowAlpha = 0.0f; + private AnimatorListenerAdapter mCircleEndListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mCircleAnimator = null; + } + }; + private AnimatorListenerAdapter mScaleEndListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mScaleAnimator = null; + } + }; + private AnimatorListenerAdapter mAlphaEndListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mAlphaAnimator = null; + } + }; + private AnimatorListenerAdapter mArrowEndListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mArrowAnimator = null; + } + }; + + public KeyguardAffordanceView(Context context) { + this(context, null); + } + + public KeyguardAffordanceView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public KeyguardAffordanceView(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public KeyguardAffordanceView(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + mCirclePaint = new Paint(); + mCirclePaint.setAntiAlias(true); + mCircleColor = 0xffffffff; + mCirclePaint.setColor(mCircleColor); + + mNormalColor = 0xffffffff; + mInverseColor = 0xff000000; + mMinBackgroundRadius = mContext.getResources().getDimensionPixelSize( + R.dimen.keyguard_affordance_min_background_radius); + mHintChevronPadding = mContext.getResources().getDimensionPixelSize( + R.dimen.hint_chevron_circle_padding); + mAppearInterpolator = AnimationUtils.loadInterpolator(mContext, + android.R.interpolator.linear_out_slow_in); + mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext, + android.R.interpolator.fast_out_linear_in); + mColorInterpolator = new ArgbEvaluator(); + mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.3f); + mArrowDrawable = context.getDrawable(R.drawable.ic_chevron_left); + mArrowDrawable.setBounds(0, 0, mArrowDrawable.getIntrinsicWidth(), + mArrowDrawable.getIntrinsicHeight()); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + mCenterX = getWidth() / 2; + mCenterY = getHeight() / 2; + } + + @Override + protected void onDraw(Canvas canvas) { + drawBackgroundCircle(canvas); + drawArrow(canvas); + canvas.save(); + updateIconColor(); + canvas.scale(mImageScale, mImageScale, getWidth() / 2, getHeight() / 2); + super.onDraw(canvas); + canvas.restore(); + } + + private void drawArrow(Canvas canvas) { + if (mArrowAlpha > 0) { + canvas.save(); + canvas.translate(mCenterX, mCenterY); + if (mIsLeft) { + canvas.scale(-1.0f, 1.0f); + } + canvas.translate(- mCircleRadius - mHintChevronPadding + - mArrowDrawable.getIntrinsicWidth() / 2, + - mArrowDrawable.getIntrinsicHeight() / 2); + mArrowDrawable.setAlpha((int) (mArrowAlpha * 255)); + mArrowDrawable.draw(canvas); + canvas.restore(); + } + } + + private void updateIconColor() { + Drawable drawable = getDrawable().mutate(); + float alpha = mCircleRadius / mMinBackgroundRadius; + alpha = Math.min(1.0f, alpha); + int color = (int) mColorInterpolator.evaluate(alpha, mNormalColor, mInverseColor); + drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP); + } + + private void drawBackgroundCircle(Canvas canvas) { + if (mCircleRadius > 0) { + updateCircleColor(); + canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mCirclePaint); + } + } + + private void updateCircleColor() { + float fraction = 0.5f + 0.5f * Math.max(0.0f, Math.min(1.0f, + (mCircleRadius - mMinBackgroundRadius) / (0.5f * mMinBackgroundRadius))); + int color = Color.argb((int) (Color.alpha(mCircleColor) * fraction), + Color.red(mCircleColor), + Color.green(mCircleColor), Color.blue(mCircleColor)); + mCirclePaint.setColor(color); + } + + public void finishAnimation(float velocity, final Runnable mAnimationEndRunnable) { + cancelAnimator(mCircleAnimator); + float maxCircleSize = getMaxCircleSize(); + ValueAnimator animatorToRadius = getAnimatorToRadius(maxCircleSize); + mFlingAnimationUtils.applyDismissing(animatorToRadius, mCircleRadius, maxCircleSize, + velocity, maxCircleSize); + animatorToRadius.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mAnimationEndRunnable.run(); + } + }); + animatorToRadius.start(); + setImageAlpha(0, true); + } + + private float getMaxCircleSize() { + getLocationInWindow(mTempPoint); + float rootWidth = getRootView().getWidth(); + float width = mTempPoint[0] + mCenterX; + width = Math.max(rootWidth - width, width); + float height = mTempPoint[1] + mCenterY; + return (float) Math.hypot(width, height); + } + + public void setCircleRadius(float circleRadius) { + setCircleRadius(circleRadius, false); + } + + public void setCircleRadiusWithoutAnimation(float circleRadius) { + cancelAnimator(mCircleAnimator); + setCircleRadius(circleRadius, true); + } + + private void setCircleRadius(float circleRadius, boolean noAnimation) { + + // Check if we need a new animation + boolean radiusHidden = (mCircleAnimator != null && mCircleWillBeHidden) + || (mCircleAnimator == null && mCircleRadius == 0.0f); + boolean nowHidden = circleRadius == 0.0f; + boolean radiusNeedsAnimation = (radiusHidden != nowHidden) && !noAnimation; + if (!radiusNeedsAnimation) { + if (mCircleAnimator == null) { + mCircleRadius = circleRadius; + invalidate(); + } else if (!mCircleWillBeHidden) { + + // We just update the end value + float diff = circleRadius - mMinBackgroundRadius; + PropertyValuesHolder[] values = mCircleAnimator.getValues(); + values[0].setFloatValues(mCircleStartValue + diff, circleRadius); + mCircleAnimator.setCurrentPlayTime(mCircleAnimator.getCurrentPlayTime()); + } + } else { + cancelAnimator(mCircleAnimator); + ValueAnimator animator = getAnimatorToRadius(circleRadius); + Interpolator interpolator = circleRadius == 0.0f + ? mDisappearInterpolator + : mAppearInterpolator; + animator.setInterpolator(interpolator); + float durationFactor = Math.abs(mCircleRadius - circleRadius) + / (float) mMinBackgroundRadius; + long duration = (long) (CIRCLE_APPEAR_DURATION * durationFactor); + duration = Math.min(duration, CIRCLE_DISAPPEAR_MAX_DURATION); + animator.setDuration(duration); + animator.start(); + } + } + + private ValueAnimator getAnimatorToRadius(float circleRadius) { + ValueAnimator animator = ValueAnimator.ofFloat(mCircleRadius, circleRadius); + mCircleAnimator = animator; + mCircleStartValue = mCircleRadius; + mCircleWillBeHidden = circleRadius == 0.0f; + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mCircleRadius = (float) animation.getAnimatedValue(); + invalidate(); + } + }); + animator.addListener(mCircleEndListener); + return animator; + } + + private void cancelAnimator(Animator animator) { + if (animator != null) { + animator.cancel(); + } + } + + public void setImageScale(float imageScale, boolean animate) { + setImageScale(imageScale, animate, -1, null); + } + + /** + * Sets the scale of the containing image + * + * @param imageScale The new Scale. + * @param animate Should an animation be performed + * @param duration If animate, whats the duration? When -1 we take the default duration + * @param interpolator If animate, whats the interpolator? When null we take the default + * interpolator. + */ + public void setImageScale(float imageScale, boolean animate, long duration, + Interpolator interpolator) { + cancelAnimator(mScaleAnimator); + if (!animate) { + mImageScale = imageScale; + invalidate(); + } else { + ValueAnimator animator = ValueAnimator.ofFloat(mImageScale, imageScale); + mScaleAnimator = animator; + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mImageScale = (float) animation.getAnimatedValue(); + invalidate(); + } + }); + animator.addListener(mScaleEndListener); + if (interpolator == null) { + interpolator = imageScale == 0.0f + ? mDisappearInterpolator + : mAppearInterpolator; + } + animator.setInterpolator(interpolator); + if (duration == -1) { + float durationFactor = Math.abs(mImageScale - imageScale) + / (1.0f - MIN_ICON_SCALE_AMOUNT); + durationFactor = Math.min(1.0f, durationFactor); + duration = (long) (NORMAL_ANIMATION_DURATION * durationFactor); + } + animator.setDuration(duration); + animator.start(); + } + } + + public void setImageAlpha(float alpha, boolean animate) { + setImageAlpha(alpha, animate, -1, null, null); + } + + /** + * Sets the alpha of the containing image + * + * @param alpha The new alpha. + * @param animate Should an animation be performed + * @param duration If animate, whats the duration? When -1 we take the default duration + * @param interpolator If animate, whats the interpolator? When null we take the default + * interpolator. + */ + public void setImageAlpha(float alpha, boolean animate, long duration, + Interpolator interpolator, Runnable runnable) { + cancelAnimator(mAlphaAnimator); + int endAlpha = (int) (alpha * 255); + if (!animate) { + setImageAlpha(endAlpha); + } else { + int currentAlpha = getImageAlpha(); + ValueAnimator animator = ValueAnimator.ofInt(currentAlpha, endAlpha); + mAlphaAnimator = animator; + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + setImageAlpha((int) animation.getAnimatedValue()); + } + }); + animator.addListener(mAlphaEndListener); + if (interpolator == null) { + interpolator = alpha == 0.0f + ? mDisappearInterpolator + : mAppearInterpolator; + } + animator.setInterpolator(interpolator); + if (duration == -1) { + float durationFactor = Math.abs(currentAlpha - endAlpha) / 255f; + durationFactor = Math.min(1.0f, durationFactor); + duration = (long) (NORMAL_ANIMATION_DURATION * durationFactor); + } + animator.setDuration(duration); + if (runnable != null) { + animator.addListener(getEndListener(runnable)); + } + animator.start(); + } + } + + private Animator.AnimatorListener getEndListener(final Runnable runnable) { + return new AnimatorListenerAdapter() { + boolean mCancelled; + @Override + public void onAnimationCancel(Animator animation) { + mCancelled = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + if (!mCancelled) { + runnable.run(); + } + } + }; + } + + public float getCircleRadius() { + return mCircleRadius; + } + + public void showArrow(boolean show) { + cancelAnimator(mArrowAnimator); + float targetAlpha = show ? 1.0f : 0.0f; + if (mArrowAlpha == targetAlpha) { + return; + } + ValueAnimator animator = ValueAnimator.ofFloat(mArrowAlpha, targetAlpha); + mArrowAnimator = animator; + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mArrowAlpha = (float) animation.getAnimatedValue(); + invalidate(); + } + }); + animator.addListener(mArrowEndListener); + Interpolator interpolator = show + ? mAppearInterpolator + : mDisappearInterpolator; + animator.setInterpolator(interpolator); + float durationFactor = Math.abs(mArrowAlpha - targetAlpha); + long duration = (long) (NORMAL_ANIMATION_DURATION * durationFactor); + animator.setDuration(duration); + animator.start(); + } + + public void setIsLeft(boolean left) { + mIsLeft = left; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java index d5f9619..a8a0cb1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java @@ -21,28 +21,28 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.content.Context; import android.os.PowerManager; +import android.os.SystemClock; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; 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; - -import java.util.ArrayList; +import com.android.systemui.statusbar.KeyguardAffordanceView; /** - * A touch handler of the Keyguard which is responsible for swiping the content left or right. + * A touch handler of the keyguard which is responsible for launching phone and camera affordances. */ -public class KeyguardPageSwipeHelper { +public class KeyguardAffordanceHelper { - private static final float SWIPE_MAX_ICON_SCALE_AMOUNT = 2.0f; public static final float SWIPE_RESTING_ALPHA_AMOUNT = 0.5f; - public static final long HINT_PHASE1_DURATION = 250; - private static final long HINT_PHASE2_DURATION = 450; + public static final long HINT_PHASE1_DURATION = 200; + private static final long HINT_PHASE2_DURATION = 350; + private static final float BACKGROUND_RADIUS_SCALE_FACTOR = 0.15f; + private static final int HINT_CIRCLE_OPEN_DURATION = 500; private final Context mContext; @@ -58,25 +58,42 @@ public class KeyguardPageSwipeHelper { private int mTouchSlop; private int mMinTranslationAmount; private int mMinFlingVelocity; - private int mHintDistance; - private final View mLeftIcon; - private final View mCenterIcon; - private final View mRightIcon; - private Interpolator mFastOutSlowIn; - private Interpolator mBounceInterpolator; + private int mHintGrowAmount; + private final KeyguardAffordanceView mLeftIcon; + private final KeyguardAffordanceView mCenterIcon; + private final KeyguardAffordanceView mRightIcon; + private Interpolator mAppearInterpolator; + private Interpolator mDisappearInterpolator; private Animator mSwipeAnimator; - private boolean mCallbackCalled; + private int mMinBackgroundRadius; + private boolean mMotionPerformedByUser; + private PowerManager mPM; + private AnimatorListenerAdapter mFlingEndListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mSwipeAnimator = null; + setSwipingInProgress(false); + } + }; + private Runnable mAnimationEndRunnable = new Runnable() { + @Override + public void run() { + mCallback.onAnimationToSideEnded(); + } + }; - KeyguardPageSwipeHelper(Callback callback, Context context) { + KeyguardAffordanceHelper(Callback callback, Context context) { mContext = context; mCallback = callback; mLeftIcon = mCallback.getLeftIcon(); + mLeftIcon.setIsLeft(true); mCenterIcon = mCallback.getCenterIcon(); mRightIcon = mCallback.getRightIcon(); - updateIcon(mLeftIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false); - updateIcon(mCenterIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false); - updateIcon(mRightIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false); + updateIcon(mLeftIcon, 0.0f, SWIPE_RESTING_ALPHA_AMOUNT, false); + updateIcon(mCenterIcon, 0.0f, SWIPE_RESTING_ALPHA_AMOUNT, false); + updateIcon(mRightIcon, 0.0f, SWIPE_RESTING_ALPHA_AMOUNT, false); initDimens(); + mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); } private void initDimens() { @@ -85,12 +102,15 @@ 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); + mMinBackgroundRadius = mContext.getResources().getDimensionPixelSize( + R.dimen.keyguard_affordance_min_background_radius); + mHintGrowAmount = + mContext.getResources().getDimensionPixelSize(R.dimen.hint_grow_amount_sideways); mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.4f); - mFastOutSlowIn = AnimationUtils.loadInterpolator(mContext, - android.R.interpolator.fast_out_slow_in); - mBounceInterpolator = new BounceInterpolator(); + mAppearInterpolator = AnimationUtils.loadInterpolator(mContext, + android.R.interpolator.linear_out_slow_in); + mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext, + android.R.interpolator.fast_out_linear_in); } public boolean onTouchEvent(MotionEvent event) { @@ -102,16 +122,18 @@ public class KeyguardPageSwipeHelper { final float y = event.getY(pointerIndex); final float x = event.getX(pointerIndex); + boolean isUp = false; switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: if (mSwipingInProgress) { - cancelAnimations(); + cancelAnimation(); } mInitialTouchY = y; mInitialTouchX = x; mTranslationOnDown = mTranslation; initVelocityTracker(); trackMovement(event); + mMotionPerformedByUser = false; break; case MotionEvent.ACTION_POINTER_UP: @@ -135,23 +157,24 @@ public class KeyguardPageSwipeHelper { || (rightSwipePossible() && w < -mTouchSlop)) && Math.abs(w) > Math.abs(y - mInitialTouchY) && !mSwipingInProgress) { - cancelAnimations(); + cancelAnimation(); mInitialTouchY = y; mInitialTouchX = x; mTranslationOnDown = mTranslation; - mSwipingInProgress = true; + setSwipingInProgress(true); } if (mSwipingInProgress) { - setTranslation(mTranslationOnDown + x - mInitialTouchX, false); + setTranslation(mTranslationOnDown + x - mInitialTouchX, false, false); } break; case MotionEvent.ACTION_UP: + isUp = true; case MotionEvent.ACTION_CANCEL: mTrackingPointer = -1; trackMovement(event); if (mSwipingInProgress) { - flingWithCurrentVelocity(); + flingWithCurrentVelocity(!isUp); } if (mVelocityTracker != null) { mVelocityTracker.recycle(); @@ -162,6 +185,13 @@ public class KeyguardPageSwipeHelper { return true; } + private void setSwipingInProgress(boolean inProgress) { + mSwipingInProgress = inProgress; + if (inProgress) { + mCallback.onSwipingStarted(); + } + } + private boolean rightSwipePossible() { return mRightIcon.getVisibility() == View.VISIBLE; } @@ -175,22 +205,14 @@ public class KeyguardPageSwipeHelper { } 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(); - } - }); + private void startHintAnimationPhase1(final boolean right, final Runnable onFinishedListener) { + final KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon; + targetView.showArrow(true); + ValueAnimator animator = getAnimatorToRadius(right, mHintGrowAmount); animator.addListener(new AnimatorListenerAdapter() { private boolean mCancelled; @@ -204,12 +226,13 @@ public class KeyguardPageSwipeHelper { if (mCancelled) { mSwipeAnimator = null; onFinishedListener.run(); + targetView.showArrow(false); } else { - startUnlockHintAnimationPhase2(onFinishedListener); + startUnlockHintAnimationPhase2(right, onFinishedListener); } } }); - animator.setInterpolator(mFastOutSlowIn); + animator.setInterpolator(mAppearInterpolator); animator.setDuration(HINT_PHASE1_DURATION); animator.start(); mSwipeAnimator = animator; @@ -218,75 +241,69 @@ public class KeyguardPageSwipeHelper { /** * 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(); - } - }); + private void startUnlockHintAnimationPhase2(boolean right, final Runnable onFinishedListener) { + final KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon; + ValueAnimator animator = getAnimatorToRadius(right, 0); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mSwipeAnimator = null; + targetView.showArrow(false); onFinishedListener.run(); } + + @Override + public void onAnimationStart(Animator animation) { + targetView.showArrow(false); + } }); - animator.setInterpolator(mBounceInterpolator); + animator.setInterpolator(mDisappearInterpolator); animator.setDuration(HINT_PHASE2_DURATION); + animator.setStartDelay(HINT_CIRCLE_OPEN_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 ValueAnimator getAnimatorToRadius(final boolean right, int radius) { + final KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon; + ValueAnimator animator = ValueAnimator.ofFloat(targetView.getCircleRadius(), radius); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float newRadius = (float) animation.getAnimatedValue(); + targetView.setCircleRadiusWithoutAnimation(newRadius); + float translation = getTranslationFromRadius(newRadius); + mTranslation = right ? -translation : translation; + updateIconsFromRadius(targetView, newRadius); + } + }); + return animator; } - private void cancelAnimations() { - ArrayList<View> targetViews = mCallback.getTranslationViews(); - for (View target : targetViews) { - target.animate().cancel(); - } - View targetView = mTranslation > 0 ? mLeftIcon : mRightIcon; - targetView.animate().cancel(); + private void cancelAnimation() { if (mSwipeAnimator != null) { mSwipeAnimator.cancel(); - hideInactiveIcons(true); } } - private void flingWithCurrentVelocity() { + private void flingWithCurrentVelocity(boolean forceSnapBack) { float vel = getCurrentVelocity(); // We snap back if the current translation is not far enough - boolean snapBack = Math.abs(mTranslation) < mMinTranslationAmount; + boolean snapBack = Math.abs(mTranslation) < Math.abs(mTranslationOnDown) + + mMinTranslationAmount; // or if the velocity is in the opposite direction. boolean velIsInWrongDirection = vel * mTranslation < 0; snapBack |= Math.abs(vel) > mMinFlingVelocity && velIsInWrongDirection; vel = snapBack ^ velIsInWrongDirection ? 0 : vel; - fling(vel, snapBack); + fling(vel, snapBack || forceSnapBack); } private void fling(float vel, final boolean snapBack) { float target = mTranslation < 0 ? -mCallback.getPageWidth() : mCallback.getPageWidth(); target = snapBack ? 0 : target; - // translation Animation - startTranslationAnimations(vel, target); - - // animate left / right icon - startIconAnimation(vel, snapBack, target); - ValueAnimator animator = ValueAnimator.ofFloat(mTranslation, target); mFlingAnimationUtils.apply(animator, mTranslation, target, vel); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @@ -295,109 +312,84 @@ public class KeyguardPageSwipeHelper { 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) { - mSwipeAnimator = null; - mSwipingInProgress = false; - if (!snapBack && !mCallbackCalled && !mCancelled) { - - // ensure that the callback is called eventually - mCallback.onAnimationToSideStarted(mTranslation < 0); - mCallbackCalled = true; - } - } - }); + animator.addListener(mFlingEndListener); if (!snapBack) { - mCallbackCalled = false; - animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - int frameNumber; - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - if (frameNumber == 2 && !mCallbackCalled) { - - // we have to wait for the second frame for this call, - // until the render thread has definitely kicked in, to avoid a lag. - mCallback.onAnimationToSideStarted(mTranslation < 0); - mCallbackCalled = true; - } - frameNumber++; - } - }); + startFinishingCircleAnimation(vel * 0.375f, mAnimationEndRunnable); + mCallback.onAnimationToSideStarted(mTranslation < 0); } else { - showAllIcons(true); + reset(true); } animator.start(); mSwipeAnimator = animator; } - private void startTranslationAnimations(float vel, float target) { - ArrayList<View> targetViews = mCallback.getTranslationViews(); - for (View targetView : targetViews) { - ViewPropertyAnimator animator = targetView.animate(); - mFlingAnimationUtils.apply(animator, mTranslation, target, vel); - animator.translationX(target); - } - } - - private void startIconAnimation(float vel, boolean snapBack, float target) { - float scale = snapBack ? 1.0f : SWIPE_MAX_ICON_SCALE_AMOUNT; - float alpha = snapBack ? SWIPE_RESTING_ALPHA_AMOUNT : 1.0f; - View targetView = mTranslation > 0 - ? mLeftIcon - : mRightIcon; - if (targetView.getVisibility() == View.VISIBLE) { - ViewPropertyAnimator iconAnimator = targetView.animate(); - mFlingAnimationUtils.apply(iconAnimator, mTranslation, target, vel); - iconAnimator.scaleX(scale); - iconAnimator.scaleY(scale); - iconAnimator.alpha(alpha); - } + private void startFinishingCircleAnimation(float velocity, Runnable mAnimationEndRunnable) { + KeyguardAffordanceView targetView = mTranslation > 0 ? mLeftIcon : mRightIcon; + targetView.finishAnimation(velocity, mAnimationEndRunnable); } - private void setTranslation(float translation, boolean isReset) { + private void setTranslation(float translation, boolean isReset, boolean animateReset) { translation = rightSwipePossible() ? translation : Math.max(0, translation); translation = leftSwipePossible() ? translation : Math.min(0, translation); + float absTranslation = Math.abs(translation); + if (absTranslation > Math.abs(mTranslationOnDown) + mMinTranslationAmount || + mMotionPerformedByUser) { + userActivity(); + mMotionPerformedByUser = true; + } if (translation != mTranslation || isReset) { - ArrayList<View> translatedViews = mCallback.getTranslationViews(); - for (View view : translatedViews) { - view.setTranslationX(translation); - } - if (translation == 0.0f) { - boolean animate = !isReset; - showAllIcons(animate); + KeyguardAffordanceView targetView = translation > 0 ? mLeftIcon : mRightIcon; + KeyguardAffordanceView otherView = translation > 0 ? mRightIcon : mLeftIcon; + float alpha = absTranslation / mMinTranslationAmount; + + // We interpolate the alpha of the other icons to 0 + float fadeOutAlpha = SWIPE_RESTING_ALPHA_AMOUNT * (1.0f - alpha); + fadeOutAlpha = Math.max(0.0f, fadeOutAlpha); + + // We interpolate the alpha of the targetView to 1 + alpha = fadeOutAlpha + alpha; + + boolean animateIcons = isReset && animateReset; + float radius = getRadiusFromTranslation(absTranslation); + if (!isReset) { + updateIcon(targetView, radius, alpha, false); } else { - View targetView = translation > 0 ? mLeftIcon : mRightIcon; - float progress = Math.abs(translation) / mCallback.getPageWidth(); - progress = Math.min(progress, 1.0f); - float alpha = SWIPE_RESTING_ALPHA_AMOUNT * (1.0f - progress) + progress; - float scale = (1.0f - progress) + progress * SWIPE_MAX_ICON_SCALE_AMOUNT; - updateIcon(targetView, scale, alpha, false); - View otherView = translation < 0 ? mLeftIcon : mRightIcon; - if (mTranslation * translation <= 0) { - // The sign of the translation has changed so we need to hide the other icons - updateIcon(otherView, 0, 0, true); - updateIcon(mCenterIcon, 0, 0, true); - } + updateIcon(targetView, 0.0f, fadeOutAlpha, animateIcons); } + updateIcon(otherView, 0.0f, fadeOutAlpha, animateIcons); + updateIcon(mCenterIcon, 0.0f, fadeOutAlpha, animateIcons); + mTranslation = translation; } } - public void showAllIcons(boolean animate) { - float scale = 1.0f; - float alpha = SWIPE_RESTING_ALPHA_AMOUNT; - updateIcon(mRightIcon, scale, alpha, animate); - updateIcon(mCenterIcon, scale, alpha, animate); - updateIcon(mLeftIcon, scale, alpha, animate); + private void updateIconsFromRadius(KeyguardAffordanceView targetView, float newRadius) { + float alpha = newRadius / mMinBackgroundRadius; + + // We interpolate the alpha of the other icons to 0 + float fadeOutAlpha = SWIPE_RESTING_ALPHA_AMOUNT * (1.0f - alpha); + fadeOutAlpha = Math.max(0.0f, fadeOutAlpha); + + // We interpolate the alpha of the targetView to 1 + alpha = fadeOutAlpha + alpha; + KeyguardAffordanceView otherView = targetView == mRightIcon ? mLeftIcon : mRightIcon; + updateIconAlpha(targetView, alpha, false); + updateIconAlpha(otherView, fadeOutAlpha, false); + updateIconAlpha(mCenterIcon, fadeOutAlpha, false); + } + + private float getTranslationFromRadius(float circleSize) { + float translation = (circleSize - mMinBackgroundRadius) / BACKGROUND_RADIUS_SCALE_FACTOR; + return Math.max(0, translation); + } + + private float getRadiusFromTranslation(float translation) { + return translation * BACKGROUND_RADIUS_SCALE_FACTOR + mMinBackgroundRadius; + } + + + private void userActivity() { + mPM.userActivity(SystemClock.uptimeMillis(), false); } public void animateHideLeftRightIcon() { @@ -405,32 +397,26 @@ public class KeyguardPageSwipeHelper { updateIcon(mLeftIcon, 0f, 0f, true); } - private void hideInactiveIcons(boolean animate){ - View otherView = mTranslation < 0 ? mLeftIcon : mRightIcon; - updateIcon(otherView, 0, 0, animate); - updateIcon(mCenterIcon, 0, 0, animate); - } - - private void updateIcon(View view, float scale, float alpha, boolean animate) { + private void updateIcon(KeyguardAffordanceView view, float circleRadius, float alpha, + boolean animate) { if (view.getVisibility() != View.VISIBLE) { return; } - if (!animate) { - view.animate().cancel(); - view.setAlpha(alpha); - view.setScaleX(scale); - view.setScaleY(scale); - // TODO: remove this invalidate once the property setters invalidate it properly - view.invalidate(); - } else { - if (view.getAlpha() != alpha || view.getScaleX() != scale) { - view.animate() - .setInterpolator(mFastOutSlowIn) - .alpha(alpha) - .scaleX(scale) - .scaleY(scale); - } - } + view.setCircleRadius(circleRadius); + updateIconAlpha(view, alpha, animate); + } + + private void updateIconAlpha(KeyguardAffordanceView view, float alpha, boolean animate) { + float scale = getScale(alpha); + alpha = Math.min(1.0f, alpha); + view.setImageAlpha(alpha, animate); + view.setImageScale(scale, animate); + } + + private float getScale(float alpha) { + float scale = alpha / SWIPE_RESTING_ALPHA_AMOUNT * 0.2f + + KeyguardAffordanceView.MIN_ICON_SCALE_AMOUNT; + return Math.min(scale, KeyguardAffordanceView.MAX_ICON_SCALE_AMOUNT); } private void trackMovement(MotionEvent event) { @@ -458,20 +444,12 @@ public class KeyguardPageSwipeHelper { initDimens(); } - public void reset() { + public void reset(boolean animate) { if (mSwipeAnimator != null) { mSwipeAnimator.cancel(); } - ArrayList<View> targetViews = mCallback.getTranslationViews(); - for (View view : targetViews) { - view.animate().cancel(); - } - setTranslation(0.0f, true); - mSwipingInProgress = false; - } - - public boolean isSwipingInProgress() { - return mSwipingInProgress; + setTranslation(0.0f, true, animate); + setSwipingInProgress(false); } public interface Callback { @@ -483,14 +461,19 @@ public class KeyguardPageSwipeHelper { */ void onAnimationToSideStarted(boolean rightPage); + /** + * Notifies the callback the animation to a side page has ended. + */ + void onAnimationToSideEnded(); + float getPageWidth(); - ArrayList<View> getTranslationViews(); + void onSwipingStarted(); - View getLeftIcon(); + KeyguardAffordanceView getLeftIcon(); - View getCenterIcon(); + KeyguardAffordanceView getCenterIcon(); - View getRightIcon(); + KeyguardAffordanceView getRightIcon(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 74bc698..b9f012c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -39,6 +39,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.R; import com.android.systemui.statusbar.policy.FlashlightController; +import com.android.systemui.statusbar.KeyguardAffordanceView; /** * Implementation for the bottom area of the Keyguard, including camera/phone affordance and status @@ -56,9 +57,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA); private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL); - private ImageView mCameraImageView; - private ImageView mPhoneImageView; - private ImageView mLockIcon; + private KeyguardAffordanceView mCameraImageView; + private KeyguardAffordanceView mPhoneImageView; + private KeyguardAffordanceView mLockIcon; private View mIndicationText; private ActivityStarter mActivityStarter; @@ -87,9 +88,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL protected void onFinishInflate() { super.onFinishInflate(); mLockPatternUtils = new LockPatternUtils(mContext); - mCameraImageView = (ImageView) findViewById(R.id.camera_button); - mPhoneImageView = (ImageView) findViewById(R.id.phone_button); - mLockIcon = (ImageView) findViewById(R.id.lock_icon); + mCameraImageView = (KeyguardAffordanceView) findViewById(R.id.camera_button); + mPhoneImageView = (KeyguardAffordanceView) findViewById(R.id.phone_button); + mLockIcon = (KeyguardAffordanceView) findViewById(R.id.lock_icon); mIndicationText = findViewById(R.id.keyguard_indication_text); watchForCameraPolicyChanges(); watchForAccessibilityChanges(); @@ -98,6 +99,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mUnlockMethodCache = UnlockMethodCache.getInstance(getContext()); mUnlockMethodCache.addListener(this); updateTrust(); + setClipChildren(false); + setClipToPadding(false); } public void setActivityStarter(ActivityStarter activityStarter) { @@ -228,15 +231,15 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mLockIcon.setImageResource(iconRes); } - public ImageView getPhoneImageView() { + public KeyguardAffordanceView getPhoneView() { return mPhoneImageView; } - public ImageView getCameraImageView() { + public KeyguardAffordanceView getCameraView() { return mCameraImageView; } - public ImageView getLockIcon() { + public KeyguardAffordanceView getLockIcon() { return mLockIcon; } 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 fc0f2d5..11a38b5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -39,6 +39,7 @@ import com.android.systemui.qs.QSPanel; import com.android.systemui.statusbar.ExpandableView; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.statusbar.GestureRecorder; +import com.android.systemui.statusbar.KeyguardAffordanceView; import com.android.systemui.statusbar.MirrorView; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; @@ -49,7 +50,7 @@ import java.util.ArrayList; public class NotificationPanelView extends PanelView implements ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener, View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener, - KeyguardPageSwipeHelper.Callback { + KeyguardAffordanceHelper.Callback { // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is // changed. @@ -59,7 +60,7 @@ public class NotificationPanelView extends PanelView implements private static final float HEADER_RUBBERBAND_FACTOR = 2.15f; private static final float LOCK_ICON_ACTIVE_SCALE = 1.2f; - private KeyguardPageSwipeHelper mPageSwiper; + private KeyguardAffordanceHelper mAfforanceHelper; private StatusBarHeaderView mHeader; private View mQsContainer; private QSPanel mQsPanel; @@ -124,7 +125,6 @@ public class NotificationPanelView extends PanelView implements private boolean mIsExpanding; private boolean mBlockTouches; - private ArrayList<View> mSwipeTranslationViews = new ArrayList<>(); private int mNotificationScrimWaitDistance; private boolean mTwoFingerQsExpand; private boolean mTwoFingerQsExpandPossible; @@ -135,6 +135,10 @@ public class NotificationPanelView extends PanelView implements */ private int mScrollYOverride = -1; private boolean mQsAnimatorExpand; + private boolean mIsLaunchTransitionFinished; + private boolean mIsLaunchTransitionRunning; + private Runnable mLaunchAnimationEndRunnable; + private boolean mOnlyAffordanceInThisMotion; public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); @@ -167,9 +171,7 @@ public class NotificationPanelView extends PanelView implements mFastOutLinearInterpolator = AnimationUtils.loadInterpolator(getContext(), android.R.interpolator.fast_out_linear_in); mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area); - mSwipeTranslationViews.add(mNotificationStackScroller); - mSwipeTranslationViews.add(mKeyguardStatusView); - mPageSwiper = new KeyguardPageSwipeHelper(this, getContext()); + mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext()); } @Override @@ -297,9 +299,10 @@ public class NotificationPanelView extends PanelView implements @Override public void resetViews() { + mIsLaunchTransitionFinished = false; mBlockTouches = false; mUnlockIconActive = false; - mPageSwiper.reset(); + mAfforanceHelper.reset(true); closeQs(); mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, false /* animate */, true /* cancelAnimators */); @@ -354,6 +357,7 @@ public class NotificationPanelView extends PanelView implements if (mBlockTouches) { return false; } + resetDownStates(event); int pointerIndex = event.findPointerIndex(mTrackingPointer); if (pointerIndex < 0) { pointerIndex = 0; @@ -430,6 +434,12 @@ public class NotificationPanelView extends PanelView implements return !mQsExpanded && super.onInterceptTouchEvent(event); } + private void resetDownStates(MotionEvent event) { + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + mOnlyAffordanceInThisMotion = false; + } + } + @Override public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { @@ -464,15 +474,14 @@ public class NotificationPanelView extends PanelView implements if (mBlockTouches) { return false; } - // TODO: Handle doublefinger swipe to notifications again. Look at history for a reference - // implementation. + resetDownStates(event); if ((!mIsExpanding || mHintAnimationRunning) && !mQsExpanded && mStatusBar.getBarState() != StatusBarState.SHADE) { - mPageSwiper.onTouchEvent(event); - if (mPageSwiper.isSwipingInProgress()) { - return true; - } + mAfforanceHelper.onTouchEvent(event); + } + if (mOnlyAffordanceInThisMotion) { + return true; } if (event.getActionMasked() == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f && mStatusBar.getBarState() != StatusBarState.KEYGUARD) { @@ -951,20 +960,16 @@ public class NotificationPanelView extends PanelView implements if (mStatusBar.getBarState() == StatusBarState.KEYGUARD || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) { boolean active = getMaxPanelHeight() - getExpandedHeight() > mUnlockMoveDistance; + KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon(); if (active && !mUnlockIconActive && mTracking) { - mKeyguardBottomArea.getLockIcon().animate() - .alpha(1f) - .scaleY(LOCK_ICON_ACTIVE_SCALE) - .scaleX(LOCK_ICON_ACTIVE_SCALE) - .setInterpolator(mFastOutLinearInterpolator) - .setDuration(150); + lockIcon.setImageAlpha(1.0f, true, 150, mFastOutLinearInterpolator, null); + lockIcon.setImageScale(LOCK_ICON_ACTIVE_SCALE, true, 150, + mFastOutLinearInterpolator); } else if (!active && mUnlockIconActive && mTracking) { - mKeyguardBottomArea.getLockIcon().animate() - .alpha(KeyguardPageSwipeHelper.SWIPE_RESTING_ALPHA_AMOUNT) - .scaleY(1f) - .scaleX(1f) - .setInterpolator(mFastOutLinearInterpolator) - .setDuration(150); + lockIcon.setImageAlpha(KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT, true, + 150, mFastOutLinearInterpolator, null); + lockIcon.setImageScale(1.0f, true, 150, + mFastOutLinearInterpolator); } mUnlockIconActive = active; } @@ -1093,7 +1098,7 @@ public class NotificationPanelView extends PanelView implements super.onTrackingStarted(); if (mStatusBar.getBarState() == StatusBarState.KEYGUARD || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) { - mPageSwiper.animateHideLeftRightIcon(); + mAfforanceHelper.animateHideLeftRightIcon(); } } @@ -1106,16 +1111,15 @@ public class NotificationPanelView extends PanelView implements } if (expand && (mStatusBar.getBarState() == StatusBarState.KEYGUARD || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED)) { - mPageSwiper.showAllIcons(true); + if (!mHintAnimationRunning) { + mAfforanceHelper.reset(true); + } } if (!expand && (mStatusBar.getBarState() == StatusBarState.KEYGUARD || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED)) { - mKeyguardBottomArea.getLockIcon().animate() - .alpha(0f) - .scaleX(2f) - .scaleY(2f) - .setInterpolator(mFastOutLinearInterpolator) - .setDuration(100); + KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon(); + lockIcon.setImageAlpha(0.0f, true, 100, mFastOutLinearInterpolator, null); + lockIcon.setImageScale(2.0f, true, 100, mFastOutLinearInterpolator); } } @@ -1141,7 +1145,7 @@ public class NotificationPanelView extends PanelView implements @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - mPageSwiper.onConfigurationChanged(); + mAfforanceHelper.onConfigurationChanged(); } @Override @@ -1159,6 +1163,8 @@ public class NotificationPanelView extends PanelView implements @Override public void onAnimationToSideStarted(boolean rightPage) { boolean start = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? rightPage : !rightPage; + mIsLaunchTransitionRunning = true; + mLaunchAnimationEndRunnable = null; if (start) { mKeyguardBottomArea.launchPhone(); } else { @@ -1168,20 +1174,29 @@ public class NotificationPanelView extends PanelView implements } @Override + public void onAnimationToSideEnded() { + mIsLaunchTransitionRunning = false; + mIsLaunchTransitionFinished = true; + if (mLaunchAnimationEndRunnable != null) { + mLaunchAnimationEndRunnable.run(); + mLaunchAnimationEndRunnable = null; + } + } + + @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() { + mAfforanceHelper.startHintAnimation(right, new Runnable() { @Override public void run() { mHintAnimationRunning = false; mStatusBar.onHintFinished(); } }); - startHighlightIconAnimation(right ? getRightIcon() : getLeftIcon()); boolean start = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? right : !right; if (start) { mStatusBar.onPhoneHintStarted(); @@ -1199,17 +1214,14 @@ public class NotificationPanelView extends PanelView implements /** * Starts the highlight (making it fully opaque) animation on an icon. */ - private void startHighlightIconAnimation(final View icon) { - icon.animate() - .alpha(1.0f) - .setDuration(KeyguardPageSwipeHelper.HINT_PHASE1_DURATION) - .setInterpolator(mFastOutSlowInInterpolator) - .withEndAction(new Runnable() { + private void startHighlightIconAnimation(final KeyguardAffordanceView icon) { + icon.setImageAlpha(1.0f, true, KeyguardAffordanceHelper.HINT_PHASE1_DURATION, + mFastOutSlowInInterpolator, new Runnable() { @Override public void run() { - icon.animate().alpha(KeyguardPageSwipeHelper.SWIPE_RESTING_ALPHA_AMOUNT) - .setDuration(KeyguardPageSwipeHelper.HINT_PHASE1_DURATION) - .setInterpolator(mFastOutSlowInInterpolator); + icon.setImageAlpha(KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT, + true, KeyguardAffordanceHelper.HINT_PHASE1_DURATION, + mFastOutSlowInInterpolator, null); } }); } @@ -1220,27 +1232,28 @@ public class NotificationPanelView extends PanelView implements } @Override - public ArrayList<View> getTranslationViews() { - return mSwipeTranslationViews; + public void onSwipingStarted() { + requestDisallowInterceptTouchEvent(true); + mOnlyAffordanceInThisMotion = true; } @Override - public View getLeftIcon() { + public KeyguardAffordanceView getLeftIcon() { return getLayoutDirection() == LAYOUT_DIRECTION_RTL - ? mKeyguardBottomArea.getCameraImageView() - : mKeyguardBottomArea.getPhoneImageView(); + ? mKeyguardBottomArea.getCameraView() + : mKeyguardBottomArea.getPhoneView(); } @Override - public View getCenterIcon() { + public KeyguardAffordanceView getCenterIcon() { return mKeyguardBottomArea.getLockIcon(); } @Override - public View getRightIcon() { + public KeyguardAffordanceView getRightIcon() { return getLayoutDirection() == LAYOUT_DIRECTION_RTL - ? mKeyguardBottomArea.getPhoneImageView() - : mKeyguardBottomArea.getCameraImageView(); + ? mKeyguardBottomArea.getPhoneView() + : mKeyguardBottomArea.getCameraView(); } @Override @@ -1282,4 +1295,16 @@ public class NotificationPanelView extends PanelView implements public boolean shouldDelayChildPressedState() { return true; } + + public boolean isLaunchTransitionFinished() { + return mIsLaunchTransitionFinished; + } + + public boolean isLaunchTransitionRunning() { + return mIsLaunchTransitionRunning; + } + + public void setLaunchTransitionEndRunnable(Runnable r) { + mLaunchAnimationEndRunnable = r; + } } 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 e4e67c9..46a32da 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -53,7 +53,6 @@ import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.Rect; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.inputmethodservice.InputMethodService; @@ -63,6 +62,7 @@ import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.MediaSessionManager; import android.media.session.PlaybackState; +import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -70,6 +70,7 @@ import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; import android.os.SystemClock; +import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; import android.service.notification.NotificationListenerService.RankingMap; @@ -201,6 +202,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) .build(); + public static final int FADE_KEYGUARD_START_DELAY = 100; + public static final int FADE_KEYGUARD_DURATION = 300; + PhoneStatusBarPolicy mIconPolicy; // These are no longer handled by the policy, because we need custom strategies for them @@ -441,6 +445,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private final ShadeUpdates mShadeUpdates = new ShadeUpdates(); private int mDrawCount; + private Runnable mLaunchTransitionEndRunnable; + private boolean mLaunchTransitionFadingAway; private static final int VISIBLE_LOCATIONS = ViewState.LOCATION_FIRST_CARD | ViewState.LOCATION_TOP_STACK_PEEKING @@ -1728,7 +1734,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } private int adjustDisableFlags(int state) { - if (mExpandedVisible || mBouncerShowing || mWaitingForKeyguardExit) { + if (!mLaunchTransitionFadingAway + && (mExpandedVisible || mBouncerShowing || mWaitingForKeyguardExit)) { state |= StatusBarManager.DISABLE_NOTIFICATION_ICONS; state |= StatusBarManager.DISABLE_SYSTEM_INFO; } @@ -2725,13 +2732,18 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, dismissKeyguardThenExecute(new OnDismissAction() { @Override public boolean onDismiss() { - try { - // Dismiss the lock screen when Settings starts. - ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); - } catch (RemoteException e) { - } - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); + AsyncTask.execute(new Runnable() { + public void run() { + try { + intent.setFlags( + Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + mContext.startActivityAsUser( + intent, new UserHandle(UserHandle.USER_CURRENT)); + mWindowManagerService.overridePendingAppTransition(null, 0, 0, null); + } catch (RemoteException e) { + } + } + }); animateCollapsePanels(); return DELAY_DISMISS_TO_ACTIVITY_LAUNCH; @@ -2795,9 +2807,20 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, }; @Override - protected void dismissKeyguardThenExecute(OnDismissAction action) { + protected void dismissKeyguardThenExecute(final OnDismissAction action) { if (mStatusBarKeyguardViewManager.isShowing()) { - mStatusBarKeyguardViewManager.dismissWithAction(action); + if (UnlockMethodCache.getInstance(mContext).isMethodInsecure() + && mNotificationPanel.isLaunchTransitionRunning()) { + action.onDismiss(); + mNotificationPanel.setLaunchTransitionEndRunnable(new Runnable() { + @Override + public void run() { + mStatusBarKeyguardViewManager.dismiss(); + } + }); + } else { + mStatusBarKeyguardViewManager.dismissWithAction(action); + } } else { action.onDismiss(); } @@ -3170,6 +3193,52 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mLeaveOpenOnKeyguardHide = false; } + public boolean isInLaunchTransition() { + return mNotificationPanel.isLaunchTransitionRunning() + || mNotificationPanel.isLaunchTransitionFinished(); + } + + /** + * Fades the content of the keyguard away after the launch transition is done. + * + * @param beforeFading the runnable to be run when the circle is fully expanded and the fading + * starts + * @param endRunnable the runnable to be run when the transition is done + */ + public void fadeKeyguardAfterLaunchTransition(final Runnable beforeFading, + final Runnable endRunnable) { + Runnable hideRunnable = new Runnable() { + @Override + public void run() { + mLaunchTransitionFadingAway = true; + if (beforeFading != null) { + beforeFading.run(); + } + mNotificationPanel.setAlpha(1); + mNotificationPanel.animate() + .alpha(0) + .setStartDelay(FADE_KEYGUARD_START_DELAY) + .setDuration(FADE_KEYGUARD_DURATION) + .withLayer() + .withEndAction(new Runnable() { + @Override + public void run() { + mNotificationPanel.setAlpha(1); + if (endRunnable != null) { + endRunnable.run(); + } + mLaunchTransitionFadingAway = false; + } + }); + } + }; + if (mNotificationPanel.isLaunchTransitionRunning()) { + mNotificationPanel.setLaunchTransitionEndRunnable(hideRunnable); + } else { + hideRunnable.run(); + } + } + public void hideKeyguard() { setBarState(StatusBarState.SHADE); if (mLeaveOpenOnKeyguardHide) { @@ -3204,8 +3273,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private void updatePublicMode() { setLockscreenPublicMode( - (mStatusBarKeyguardViewManager.isShowing() || - mStatusBarKeyguardViewManager.isOccluded()) + (mStatusBarKeyguardViewManager.isShowing() || + mStatusBarKeyguardViewManager.isOccluded()) && mStatusBarKeyguardViewManager.isSecure()); } @@ -3315,7 +3384,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private void showBouncer() { if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { - mWaitingForKeyguardExit = true; + mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing(); mStatusBarKeyguardViewManager.dismiss(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index cf930bd..eb42401 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -71,7 +71,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener { private boolean mAnimationStarted; private boolean mDozing; private int mTeasesRemaining; - private final Interpolator mInterpolator = new DecelerateInterpolator(); public ScrimController(View scrimBehind, View scrimInFront) { @@ -149,7 +148,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener { } private void updateScrims() { - if ((!mKeyguardShowing && !mBouncerShowing) || mAnimateKeyguardFadingOut) { + if (mAnimateKeyguardFadingOut) { + setScrimInFrontColor(0f); + setScrimBehindColor(0f); + }else if (!mKeyguardShowing && !mBouncerShowing) { updateScrimNormal(); setScrimInFrontColor(0); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 93dcf90..af21f25 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -176,6 +176,19 @@ public class StatusBarKeyguardViewManager { } public void setOccluded(boolean occluded) { + if (occluded && !mOccluded && mShowing) { + if (mPhoneStatusBar.isInLaunchTransition()) { + mOccluded = true; + mPhoneStatusBar.fadeKeyguardAfterLaunchTransition(null /* beforeFading */, + new Runnable() { + @Override + public void run() { + mStatusBarWindowManager.setKeyguardOccluded(true); + } + }); + return; + } + } mOccluded = occluded; mStatusBarWindowManager.setKeyguardOccluded(occluded); reset(); @@ -188,29 +201,50 @@ public class StatusBarKeyguardViewManager { /** * Hides the keyguard view */ - public void hide(long startTime, long fadeoutDuration) { + public void hide(long startTime, final long fadeoutDuration) { mShowing = false; long uptimeMillis = SystemClock.uptimeMillis(); - long delay = startTime - uptimeMillis; - if (delay < 0) { - delay = 0; + long delay = Math.max(0, startTime - uptimeMillis); + + if (mPhoneStatusBar.isInLaunchTransition() ) { + mPhoneStatusBar.fadeKeyguardAfterLaunchTransition(new Runnable() { + @Override + public void run() { + mStatusBarWindowManager.setKeyguardShowing(false); + mStatusBarWindowManager.setKeyguardFadingAway(true); + mBouncer.animateHide(PhoneStatusBar.FADE_KEYGUARD_START_DELAY, + PhoneStatusBar.FADE_KEYGUARD_DURATION); + updateStates(); + mScrimController.animateKeyguardFadingOut( + PhoneStatusBar.FADE_KEYGUARD_START_DELAY, + PhoneStatusBar.FADE_KEYGUARD_DURATION, null); + } + }, new Runnable() { + @Override + public void run() { + mPhoneStatusBar.hideKeyguard(); + mStatusBarWindowManager.setKeyguardFadingAway(false); + mViewMediatorCallback.keyguardGone(); + } + }); + } else { + mPhoneStatusBar.setKeyguardFadingAway(delay, fadeoutDuration); + mPhoneStatusBar.hideKeyguard(); + mStatusBarWindowManager.setKeyguardFadingAway(true); + mStatusBarWindowManager.setKeyguardShowing(false); + mBouncer.animateHide(delay, fadeoutDuration); + mScrimController.animateKeyguardFadingOut(delay, fadeoutDuration, new Runnable() { + @Override + public void run() { + mStatusBarWindowManager.setKeyguardFadingAway(false); + mPhoneStatusBar.finishKeyguardFadingAway(); + } + }); + mViewMediatorCallback.keyguardGone(); + updateStates(); } - mPhoneStatusBar.setKeyguardFadingAway(delay, fadeoutDuration); - mPhoneStatusBar.hideKeyguard(); - mStatusBarWindowManager.setKeyguardFadingAway(true); - mStatusBarWindowManager.setKeyguardShowing(false); - mBouncer.animateHide(delay, fadeoutDuration); - mScrimController.animateKeyguardFadingOut(delay, fadeoutDuration, new Runnable() { - @Override - public void run() { - mStatusBarWindowManager.setKeyguardFadingAway(false); - mPhoneStatusBar.finishKeyguardFadingAway(); - } - }); - mViewMediatorCallback.keyguardGone(); - updateStates(); } /** |