Add nice animation when touching the docked divider handle
To make the interaction more dynamic. Change-Id: I8fc3e6240c229753dc26122ae0994d59c4f6486e
This commit is contained in:
@@ -1,22 +0,0 @@
|
||||
<!--
|
||||
Copyright (C) 2015 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2 (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.
|
||||
-->
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<size android:height="@dimen/docked_divider_handle_height"
|
||||
android:width="@dimen/docked_divider_handle_width" />
|
||||
<corners android:radius="1dp" />
|
||||
<solid android:color="@color/docked_divider_handle" />
|
||||
</shape>
|
||||
@@ -24,10 +24,9 @@
|
||||
android:id="@+id/docked_divider_background"
|
||||
android:background="@color/docked_divider_background"/>
|
||||
|
||||
<ImageButton
|
||||
<com.android.systemui.stackdivider.DividerHandleView
|
||||
style="@style/DockedDividerHandle"
|
||||
android:id="@+id/docked_divider_handle"
|
||||
android:background="@null"
|
||||
android:src="@drawable/docked_divider_handle"/>
|
||||
android:background="@null"/>
|
||||
|
||||
</com.android.systemui.stackdivider.DividerView>
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.stackdivider;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Property;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* View for the handle in the docked stack divider.
|
||||
*/
|
||||
public class DividerHandleView extends ImageButton {
|
||||
|
||||
private final static Property<DividerHandleView, Integer> WIDTH_PROPERTY
|
||||
= new Property<DividerHandleView, Integer>(Integer.class, "width") {
|
||||
|
||||
@Override
|
||||
public Integer get(DividerHandleView object) {
|
||||
return object.mCurrentWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(DividerHandleView object, Integer value) {
|
||||
object.mCurrentWidth = value;
|
||||
object.invalidate();
|
||||
}
|
||||
};
|
||||
|
||||
private final static Property<DividerHandleView, Integer> HEIGHT_PROPERTY
|
||||
= new Property<DividerHandleView, Integer>(Integer.class, "height") {
|
||||
|
||||
@Override
|
||||
public Integer get(DividerHandleView object) {
|
||||
return object.mCurrentHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(DividerHandleView object, Integer value) {
|
||||
object.mCurrentHeight = value;
|
||||
object.invalidate();
|
||||
}
|
||||
};
|
||||
|
||||
private final Paint mPaint = new Paint();
|
||||
private final int mWidth;
|
||||
private final int mHeight;
|
||||
private final int mCircleDiameter;
|
||||
private final Interpolator mFastOutSlowInInterpolator;
|
||||
private int mCurrentWidth;
|
||||
private int mCurrentHeight;
|
||||
private AnimatorSet mAnimator;
|
||||
|
||||
public DividerHandleView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mPaint.setColor(getResources().getColor(R.color.docked_divider_handle, null));
|
||||
mPaint.setAntiAlias(true);
|
||||
mWidth = getResources().getDimensionPixelSize(R.dimen.docked_divider_handle_width);
|
||||
mHeight = getResources().getDimensionPixelSize(R.dimen.docked_divider_handle_height);
|
||||
mCurrentWidth = mWidth;
|
||||
mCurrentHeight = mHeight;
|
||||
mCircleDiameter = (mWidth + mHeight) / 3;
|
||||
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
|
||||
android.R.interpolator.fast_out_slow_in);
|
||||
}
|
||||
|
||||
public void setTouching(boolean touching, boolean animate) {
|
||||
if (mAnimator != null) {
|
||||
mAnimator.cancel();
|
||||
mAnimator = null;
|
||||
}
|
||||
if (!animate) {
|
||||
if (touching) {
|
||||
mCurrentWidth = mCircleDiameter;
|
||||
mCurrentHeight = mCircleDiameter;
|
||||
} else {
|
||||
mCurrentWidth = mWidth;
|
||||
mCurrentHeight = mHeight;
|
||||
}
|
||||
invalidate();
|
||||
} else {
|
||||
animateToTarget(touching ? mCircleDiameter : mWidth,
|
||||
touching ? mCircleDiameter : mHeight, touching);
|
||||
}
|
||||
}
|
||||
|
||||
private void animateToTarget(int targetWidth, int targetHeight, boolean touching) {
|
||||
ObjectAnimator widthAnimator = ObjectAnimator.ofInt(this, WIDTH_PROPERTY,
|
||||
mCurrentWidth, targetWidth);
|
||||
ObjectAnimator heightAnimator = ObjectAnimator.ofInt(this, HEIGHT_PROPERTY,
|
||||
mCurrentHeight, targetHeight);
|
||||
mAnimator = new AnimatorSet();
|
||||
mAnimator.playTogether(widthAnimator, heightAnimator);
|
||||
mAnimator.setDuration(touching
|
||||
? DividerView.TOUCH_ANIMATION_DURATION
|
||||
: DividerView.TOUCH_RELEASE_ANIMATION_DURATION);
|
||||
mAnimator.setInterpolator(touching
|
||||
? DividerView.TOUCH_RESPONSE_INTERPOLATOR
|
||||
: mFastOutSlowInInterpolator);
|
||||
mAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mAnimator = null;
|
||||
}
|
||||
});
|
||||
mAnimator.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
int left = getWidth() / 2 - mCurrentWidth / 2;
|
||||
int top = getHeight() / 2 - mCurrentHeight / 2;
|
||||
int radius = Math.min(mCurrentWidth, mCurrentHeight) / 2;
|
||||
canvas.drawRoundRect(left, top, left + mCurrentWidth, top + mCurrentHeight,
|
||||
radius, radius, mPaint);
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,11 @@ import static android.view.PointerIcon.STYLE_VERTICAL_DOUBLE_ARROW;
|
||||
public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
OnComputeInternalInsetsListener {
|
||||
|
||||
static final long TOUCH_ANIMATION_DURATION = 150;
|
||||
static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;
|
||||
static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
|
||||
new PathInterpolator(0.3f, 0f, 0.1f, 1f);
|
||||
|
||||
private static final String TAG = "DividerView";
|
||||
|
||||
private static final int TASK_POSITION_SAME = Integer.MAX_VALUE;
|
||||
@@ -69,7 +74,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
private static final PathInterpolator SLOWDOWN_INTERPOLATOR =
|
||||
new PathInterpolator(0.5f, 1f, 0.5f, 1f);
|
||||
|
||||
private ImageButton mHandle;
|
||||
private DividerHandleView mHandle;
|
||||
private View mBackground;
|
||||
private int mStartX;
|
||||
private int mStartY;
|
||||
@@ -93,8 +98,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
private final Rect mLastResizeRect = new Rect();
|
||||
private final WindowManagerProxy mWindowManagerProxy = WindowManagerProxy.getInstance();
|
||||
private Interpolator mFastOutSlowInInterpolator;
|
||||
private final Interpolator mTouchResponseInterpolator =
|
||||
new PathInterpolator(0.3f, 0f, 0.1f, 1f);
|
||||
private DividerWindowManager mWindowManager;
|
||||
private VelocityTracker mVelocityTracker;
|
||||
private FlingAnimationUtils mFlingAnimationUtils;
|
||||
@@ -121,7 +124,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mHandle = (ImageButton) findViewById(R.id.docked_divider_handle);
|
||||
mHandle = (DividerHandleView) findViewById(R.id.docked_divider_handle);
|
||||
mBackground = findViewById(R.id.docked_divider_background);
|
||||
mHandle.setOnTouchListener(this);
|
||||
mDividerWindowWidth = getResources().getDimensionPixelSize(
|
||||
@@ -157,7 +160,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
return mWindowManagerProxy;
|
||||
}
|
||||
|
||||
public boolean startDragging() {
|
||||
public boolean startDragging(boolean animate) {
|
||||
mHandle.setTouching(true, animate);
|
||||
mDockSide = mWindowManagerProxy.getDockSide();
|
||||
mSnapAlgorithm = new DividerSnapAlgorithm(getContext(), mFlingAnimationUtils, mDisplayWidth,
|
||||
mDisplayHeight, mDividerSize, isHorizontalDivision(), mStableInsets);
|
||||
@@ -172,6 +176,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
}
|
||||
|
||||
public void stopDragging(int position, float velocity) {
|
||||
mHandle.setTouching(false, true /* animate */);
|
||||
fling(position, velocity);
|
||||
mWindowManager.setSlippery(true);
|
||||
releaseBackground();
|
||||
@@ -192,7 +197,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
mStartX = (int) event.getX();
|
||||
mStartY = (int) event.getY();
|
||||
getLocationOnScreen(mTempInt2);
|
||||
boolean result = startDragging();
|
||||
boolean result = startDragging(true /* animate */);
|
||||
if (isHorizontalDivision()) {
|
||||
mStartPosition = mTempInt2[1] + mDividerInsets;
|
||||
} else {
|
||||
@@ -282,29 +287,33 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
mBackground.animate().scaleX(1.4f);
|
||||
}
|
||||
mBackground.animate()
|
||||
.setInterpolator(mTouchResponseInterpolator)
|
||||
.setDuration(150)
|
||||
.translationZ(mTouchElevation);
|
||||
.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR)
|
||||
.setDuration(TOUCH_ANIMATION_DURATION)
|
||||
.translationZ(mTouchElevation)
|
||||
.start();
|
||||
|
||||
// Lift handle as well so it doesn't get behind the background, even though it doesn't
|
||||
// cast shadow.
|
||||
mHandle.animate()
|
||||
.setInterpolator(mTouchResponseInterpolator)
|
||||
.setDuration(150)
|
||||
.translationZ(mTouchElevation);
|
||||
.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR)
|
||||
.setDuration(TOUCH_ANIMATION_DURATION)
|
||||
.translationZ(mTouchElevation)
|
||||
.start();
|
||||
}
|
||||
|
||||
private void releaseBackground() {
|
||||
mBackground.animate()
|
||||
.setInterpolator(mFastOutSlowInInterpolator)
|
||||
.setDuration(200)
|
||||
.setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
|
||||
.translationZ(0)
|
||||
.scaleX(1f)
|
||||
.scaleY(1f);
|
||||
.scaleY(1f)
|
||||
.start();
|
||||
mHandle.animate()
|
||||
.setInterpolator(mFastOutSlowInInterpolator)
|
||||
.setDuration(200)
|
||||
.translationZ(0);
|
||||
.setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
|
||||
.translationZ(0)
|
||||
.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -191,7 +191,7 @@ public class NavigationBarGestureHelper extends GestureDetector.SimpleOnGestureL
|
||||
mRecentsComponent.dockTopTask(mDragMode == DRAG_MODE_RECENTS, createMode,
|
||||
initialBounds);
|
||||
if (mDragMode == DRAG_MODE_DIVIDER) {
|
||||
mDivider.getView().startDragging();
|
||||
mDivider.getView().startDragging(false /* animate */);
|
||||
}
|
||||
mDockWindowTouchSlopExceeded = true;
|
||||
MetricsLogger.action(mContext,
|
||||
|
||||
Reference in New Issue
Block a user