Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fcb1dd18 authored by Tony Wickham's avatar Tony Wickham Committed by Android (Google) Code Review
Browse files

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

parents b74ac8d3 8643cd4e
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -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>
+25 −0
Original line number Diff line number Diff line
@@ -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);
        }
    }
}
+127 −2
Original line number Diff line number Diff line
@@ -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();
    }
}