Loading quickstep/res/drawable/task_thumbnail_background.xml +0 −1 Original line number Diff line number Diff line Loading @@ -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> quickstep/src/com/android/quickstep/RecentsView.java +25 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.quickstep; import android.animation.LayoutTransition; import android.animation.TimeInterpolator; import android.content.Context; import android.graphics.Rect; Loading @@ -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; Loading Loading @@ -58,6 +60,7 @@ public class RecentsView extends PagedView { private boolean mOverviewStateEnabled; private boolean mTaskStackListenerRegistered; private LayoutTransition mLayoutTransition; private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() { @Override Loading Loading @@ -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 Loading Loading @@ -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); Loading @@ -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--) { Loading Loading @@ -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); } } } quickstep/src/com/android/quickstep/TaskView.java +127 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); Loading @@ -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 Loading @@ -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}. */ Loading Loading @@ -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
quickstep/res/drawable/task_thumbnail_background.xml +0 −1 Original line number Diff line number Diff line Loading @@ -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>
quickstep/src/com/android/quickstep/RecentsView.java +25 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.quickstep; import android.animation.LayoutTransition; import android.animation.TimeInterpolator; import android.content.Context; import android.graphics.Rect; Loading @@ -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; Loading Loading @@ -58,6 +60,7 @@ public class RecentsView extends PagedView { private boolean mOverviewStateEnabled; private boolean mTaskStackListenerRegistered; private LayoutTransition mLayoutTransition; private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() { @Override Loading Loading @@ -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 Loading Loading @@ -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); Loading @@ -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--) { Loading Loading @@ -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); } } }
quickstep/src/com/android/quickstep/TaskView.java +127 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); Loading @@ -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 Loading @@ -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}. */ Loading Loading @@ -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(); } }