Merge "Implement swipe to dismiss recent tasks" into ub-launcher3-master

This commit is contained in:
Tony Wickham 2017-12-08 19:12:42 +00:00 committed by Android (Google) Code Review
commit fcb1dd183c
3 changed files with 152 additions and 3 deletions

View File

@ -14,6 +14,5 @@
limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="#FF000000" />
<corners android:radius="2dp" />
</shape>

View File

@ -16,6 +16,7 @@
package com.android.quickstep;
import android.animation.LayoutTransition;
import android.animation.TimeInterpolator;
import android.content.Context;
import android.graphics.Rect;
@ -25,6 +26,7 @@ import android.view.View;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.dragndrop.DragLayer;
@ -58,6 +60,7 @@ public class RecentsView extends PagedView {
private boolean mOverviewStateEnabled;
private boolean mTaskStackListenerRegistered;
private LayoutTransition mLayoutTransition;
private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
@Override
@ -87,6 +90,18 @@ public class RecentsView extends PagedView {
setWillNotDraw(false);
setPageSpacing((int) getResources().getDimension(R.dimen.recents_page_spacing));
enableFreeScroll(true);
setupLayoutTransition();
}
private void setupLayoutTransition() {
// We want to show layout transitions when pages are deleted, to close the gap.
mLayoutTransition = new LayoutTransition();
mLayoutTransition.enableTransitionType(LayoutTransition.DISAPPEARING);
mLayoutTransition.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
mLayoutTransition.disableTransitionType(LayoutTransition.APPEARING);
mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
setLayoutTransition(mLayoutTransition);
}
@Override
@ -141,6 +156,7 @@ public class RecentsView extends PagedView {
// necessary)
final LayoutInflater inflater = LayoutInflater.from(getContext());
final ArrayList<Task> tasks = stack.getTasks();
setLayoutTransition(null);
for (int i = getChildCount(); i < tasks.size(); i++) {
final TaskView taskView = (TaskView) inflater.inflate(R.layout.task, this, false);
addView(taskView);
@ -150,6 +166,7 @@ public class RecentsView extends PagedView {
removeView(taskView);
loader.unloadTaskData(taskView.getTask());
}
setLayoutTransition(mLayoutTransition);
// Rebind all task views
for (int i = tasks.size() - 1; i >= 0; i--) {
@ -248,4 +265,12 @@ public class RecentsView extends PagedView {
}
}
}
public void onTaskDismissed(TaskView taskView) {
ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id);
removeView(taskView);
if (getChildCount() == 0) {
Launcher.getLauncher(getContext()).getStateManager().goToState(LauncherState.NORMAL);
}
}
}

View File

@ -16,15 +16,25 @@
package com.android.quickstep;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.ActivityOptions;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Property;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.android.launcher3.R;
import com.android.launcher3.uioverrides.OverviewState;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.touch.SwipeDetector;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskCallbacks;
import com.android.systemui.shared.recents.model.ThumbnailData;
@ -39,11 +49,38 @@ import java.util.List;
/**
* A task in the Recents view.
*/
public class TaskView extends FrameLayout implements TaskCallbacks {
public class TaskView extends FrameLayout implements TaskCallbacks, SwipeDetector.Listener {
private static final int SWIPE_DIRECTIONS = SwipeDetector.DIRECTION_POSITIVE;
/**
* The task will appear fully dismissed when the distance swiped
* reaches this percentage of the card height.
*/
private static final float SWIPE_DISTANCE_HEIGHT_PERCENTAGE = 0.38f;
private static final Property<TaskView, Float> PROPERTY_SWIPE_PROGRESS =
new Property<TaskView, Float>(Float.class, "swipe_progress") {
@Override
public Float get(TaskView taskView) {
return taskView.mSwipeProgress;
}
@Override
public void set(TaskView taskView, Float progress) {
taskView.setSwipeProgress(progress);
}
};
private Task mTask;
private TaskThumbnailView mSnapshotView;
private ImageView mIconView;
private SwipeDetector mSwipeDetector;
private float mSwipeDistance;
private float mSwipeProgress;
private Interpolator mAlphaInterpolator;
private Interpolator mSwipeAnimInterpolator;
public TaskView(Context context) {
this(context, null);
@ -58,6 +95,11 @@ public class TaskView extends FrameLayout implements TaskCallbacks {
setOnClickListener((view) -> {
launchTask(true /* animate */);
});
mSwipeDetector = new SwipeDetector(getContext(), this, SwipeDetector.VERTICAL);
mSwipeDetector.setDetectableScrollConditions(SWIPE_DIRECTIONS, false);
mAlphaInterpolator = Interpolators.ACCEL_1_5;
mSwipeAnimInterpolator = Interpolators.SCROLL_CUBIC;
}
@Override
@ -67,6 +109,15 @@ public class TaskView extends FrameLayout implements TaskCallbacks {
mIconView = findViewById(R.id.icon);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
View p = (View) getParent();
mSwipeDistance = (getMeasuredHeight() - p.getPaddingTop() - p.getPaddingBottom())
* SWIPE_DISTANCE_HEIGHT_PERCENTAGE;
}
/**
* Updates this task view to the given {@param task}.
*/
@ -134,4 +185,78 @@ public class TaskView extends FrameLayout implements TaskCallbacks {
public void onTaskWindowingModeChanged() {
// Do nothing
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
mSwipeDetector.onTouchEvent(ev);
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mSwipeDetector.onTouchEvent(event);
return mSwipeDetector.isDraggingOrSettling() || super.onTouchEvent(event);
}
// Swipe detector methods
@Override
public void onDragStart(boolean start) {
getParent().requestDisallowInterceptTouchEvent(true);
}
@Override
public boolean onDrag(float displacement, float velocity) {
setSwipeProgress(Utilities.boundToRange(displacement / mSwipeDistance,
allowsSwipeUp() ? -1 : 0, allowsSwipeDown() ? 1 : 0));
return true;
}
/**
* Indicates the page is being removed.
* @param progress Ranges from -1 (fading upwards) to 1 (fading downwards).
*/
private void setSwipeProgress(float progress) {
mSwipeProgress = progress;
float translationY = mSwipeProgress * mSwipeDistance;
float alpha = 1f - mAlphaInterpolator.getInterpolation(Math.abs(mSwipeProgress));
// Only change children to avoid changing our properties while dragging.
mIconView.setTranslationY(translationY);
mSnapshotView.setTranslationY(translationY);
mIconView.setAlpha(alpha);
mSnapshotView.setAlpha(alpha);
}
private boolean allowsSwipeUp() {
return (SWIPE_DIRECTIONS & SwipeDetector.DIRECTION_POSITIVE) != 0;
}
private boolean allowsSwipeDown() {
return (SWIPE_DIRECTIONS & SwipeDetector.DIRECTION_NEGATIVE) != 0;
}
@Override
public void onDragEnd(float velocity, boolean fling) {
boolean movingAwayFromCenter = velocity < 0 == mSwipeProgress < 0;
boolean flingAway = fling && movingAwayFromCenter
&& (allowsSwipeUp() && velocity < 0 || allowsSwipeDown() && velocity > 0);
final boolean shouldRemove = flingAway || (!fling && Math.abs(mSwipeProgress) > 0.5f);
float fromProgress = mSwipeProgress;
float toProgress = !shouldRemove ? 0f : mSwipeProgress < 0 ? -1f : 1f;
ValueAnimator swipeAnimator = ObjectAnimator.ofFloat(this, PROPERTY_SWIPE_PROGRESS,
fromProgress, toProgress);
swipeAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (shouldRemove) {
((RecentsView) getParent()).onTaskDismissed(TaskView.this);
}
mSwipeDetector.finishedScrolling();
}
});
swipeAnimator.setDuration(SwipeDetector.calculateDuration(velocity,
Math.abs(toProgress - fromProgress)));
swipeAnimator.setInterpolator(mSwipeAnimInterpolator);
swipeAnimator.start();
}
}