Merge "Implement swipe to dismiss recent tasks" into ub-launcher3-master
This commit is contained in:
commit
fcb1dd183c
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue