diff --git a/quickstep/res/layout/fallback_recents_activity.xml b/quickstep/res/layout/fallback_recents_activity.xml index 22f8b55a2a..7ecab32016 100644 --- a/quickstep/res/layout/fallback_recents_activity.xml +++ b/quickstep/res/layout/fallback_recents_activity.xml @@ -20,13 +20,23 @@ android:layout_height="match_parent" android:fitsSystemWindows="true"> - + > + - \ No newline at end of file + + + + + diff --git a/quickstep/res/layout/overview_clear_all_button.xml b/quickstep/res/layout/overview_clear_all_button.xml new file mode 100644 index 0000000000..8632f8bbd0 --- /dev/null +++ b/quickstep/res/layout/overview_clear_all_button.xml @@ -0,0 +1,14 @@ + + + \ No newline at end of file diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml index 89e057148f..6102c38aaf 100644 --- a/quickstep/res/layout/overview_panel.xml +++ b/quickstep/res/layout/overview_panel.xml @@ -14,14 +14,23 @@ See the License for the specific language governing permissions and limitations under the License. --> - +> + - \ No newline at end of file + + + + \ No newline at end of file diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index c741913953..0199cd9537 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -45,4 +45,7 @@ 10dp + + + 168dp diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml index bafa294f42..34cc0b7245 100644 --- a/quickstep/res/values/strings.xml +++ b/quickstep/res/values/strings.xml @@ -35,4 +35,7 @@ Close + + + Clear all \ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java index 124ec202d1..49d493145c 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java @@ -20,7 +20,7 @@ import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_Y_FACTOR; import static com.android.quickstep.views.RecentsView.ADJACENT_SCALE; -import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA; +import static com.android.quickstep.views.RecentsViewContainer.CONTENT_ALPHA; import android.animation.ValueAnimator; import android.annotation.TargetApi; @@ -33,21 +33,24 @@ import com.android.launcher3.LauncherStateManager.StateHandler; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.PropertySetter; import com.android.quickstep.views.LauncherRecentsView; +import com.android.quickstep.views.RecentsViewContainer; @TargetApi(Build.VERSION_CODES.O) public class RecentsViewStateController implements StateHandler { private final Launcher mLauncher; private final LauncherRecentsView mRecentsView; + private final RecentsViewContainer mRecentsViewContainer; public RecentsViewStateController(Launcher launcher) { mLauncher = launcher; mRecentsView = launcher.getOverviewPanel(); + mRecentsViewContainer = launcher.getOverviewPanelContainer(); } @Override public void setState(LauncherState state) { - mRecentsView.setContentAlpha(state.overviewUi ? 1 : 0); + mRecentsViewContainer.setContentAlpha(state.overviewUi ? 1 : 0); float[] scaleTranslationYFactor = state.getOverviewScaleAndTranslationYFactor(mLauncher); mRecentsView.setAdjacentScale(scaleTranslationYFactor[0]); mRecentsView.setTranslationYFactor(scaleTranslationYFactor[1]); @@ -66,7 +69,7 @@ public class RecentsViewStateController implements StateHandler { builder.getInterpolator(ANIM_OVERVIEW_TRANSLATION, LINEAR)); setter.setFloat(mRecentsView, TRANSLATION_Y_FACTOR, scaleTranslationYFactor[1], builder.getInterpolator(ANIM_OVERVIEW_TRANSLATION, LINEAR)); - setter.setFloat(mRecentsView, CONTENT_ALPHA, toState.overviewUi ? 1 : 0, + setter.setFloat(mRecentsViewContainer, CONTENT_ALPHA, toState.overviewUi ? 1 : 0, AGGRESSIVE_EASE_IN_OUT); if (!toState.overviewUi) { diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 597e33368e..c602392d03 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -87,21 +87,10 @@ import java.util.ArrayList; public abstract class RecentsView extends PagedView implements OnSharedPreferenceChangeListener, Insettable { + public static final boolean DEBUG_SHOW_CLEAR_ALL_BUTTON = false; + private final Rect mTempRect = new Rect(); - public static final FloatProperty CONTENT_ALPHA = - new FloatProperty("contentAlpha") { - @Override - public void setValue(RecentsView recentsView, float v) { - recentsView.setContentAlpha(v); - } - - @Override - public Float get(RecentsView recentsView) { - return recentsView.mContentAlpha; - } - }; - public static final FloatProperty ADJACENT_SCALE = new FloatProperty("adjacentScale") { @Override @@ -180,6 +169,8 @@ public abstract class RecentsView // Keeps track of task views whose visual state should not be reset private ArraySet mIgnoreResetTaskViews = new ArraySet<>(); + private RecentsViewContainer mContainerView; + // Variables for empty state private final Drawable mEmptyIcon; private final CharSequence mEmptyMessage; @@ -320,12 +311,18 @@ public abstract class RecentsView @Override public boolean onTouchEvent(MotionEvent ev) { - super.onTouchEvent(ev); + if (DEBUG_SHOW_CLEAR_ALL_BUTTON && mTouchState == TOUCH_STATE_REST && mScroller.isFinished() + && getChildCount() != 0 + && ev.getX() > getChildAt(getChildCount() - 1).getRight() - getScrollX()) { + // If nothing is in motion, allow events to the right of the last task to go to the + // Clear All button. + return false; + } + if (ev.getAction() == MotionEvent.ACTION_UP && mShowEmptyMessage) { onAllTasksRemoved(); } - // Do not let touch escape to siblings below this view. - return true; + return super.onTouchEvent(ev); } private void applyLoadPlan(RecentsTaskLoadPlan loadPlan) { @@ -424,6 +421,10 @@ public abstract class RecentsView protected abstract void getTaskSize(DeviceProfile dp, Rect outRect); + public void getTaskSize(Rect outRect) { + getTaskSize(mActivity.getDeviceProfile(), outRect); + } + @Override protected boolean computeScrollHelper() { boolean scrolling = super.computeScrollHelper(); @@ -530,6 +531,7 @@ public abstract class RecentsView unloadVisibleTaskData(); setCurrentPage(0); + scrollTo(0, 0); OverviewCallbacks.get(getContext()).onResetOverview(); } @@ -844,11 +846,11 @@ public abstract class RecentsView snapToPageRelative(1); } - public void setContentAlpha(float alpha) { - if (mContentAlpha == alpha) { - return; - } + public float getContentAlpha() { + return mContentAlpha; + } + public void setContentAlpha(float alpha) { mContentAlpha = alpha; for (int i = getChildCount() - 1; i >= 0; i--) { TaskView child = getPageAt(i); @@ -860,8 +862,6 @@ public abstract class RecentsView int alphaInt = Math.round(alpha * 255); mEmptyMessagePaint.setAlpha(alphaInt); mEmptyIcon.setAlpha(alphaInt); - - setVisibility(alpha > 0 ? VISIBLE : GONE); } public void setAdjacentScale(float adjacentScale) { @@ -929,6 +929,9 @@ public abstract class RecentsView mShowEmptyMessage = isEmpty; updateEmptyStateUi(hasSizeChanged); invalidate(); + if (mContainerView != null) { + mContainerView.onEmptyStateChanged(!DEBUG_SHOW_CLEAR_ALL_BUTTON || mShowEmptyMessage); + } } @Override @@ -1095,4 +1098,30 @@ public abstract class RecentsView protected String getCurrentPageDescription() { return ""; } + + public void dismissAllTasks() { + for (int i = 0; i < getChildCount(); ++i) { + Task task = getPageAt(i).getTask(); + if (task != null) { + ActivityManagerWrapper.getInstance().removeTask(task.key.id); + } + } + onAllTasksRemoved(); + } + + @Override + protected int computeMaxScrollX() { + if (!DEBUG_SHOW_CLEAR_ALL_BUTTON || getChildCount() == 0) { + return super.computeMaxScrollX(); + } + + // Allow a clear_all_container_width-sized gap after the last task. + return super.computeMaxScrollX() + (int) getResources().getDimension( + R.dimen.clear_all_container_width) - getPaddingEnd(); + } + + public void setContainerView(RecentsViewContainer containerView) { + mContainerView = containerView; + mContainerView.onEmptyStateChanged(!DEBUG_SHOW_CLEAR_ALL_BUTTON || mShowEmptyMessage); + } } diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java new file mode 100644 index 0000000000..cd4a6ffc58 --- /dev/null +++ b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java @@ -0,0 +1,82 @@ +package com.android.quickstep.views; + +import android.content.Context; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.util.FloatProperty; +import android.view.MotionEvent; +import android.view.View; + +import com.android.launcher3.InsettableFrameLayout; +import com.android.launcher3.R; + +public class RecentsViewContainer extends InsettableFrameLayout { + public static final FloatProperty CONTENT_ALPHA = + new FloatProperty("contentAlpha") { + @Override + public void setValue(RecentsViewContainer view, float v) { + view.setContentAlpha(v); + } + + @Override + public Float get(RecentsViewContainer view) { + return view.mRecentsView.getContentAlpha(); + } + }; + + private final Rect mTempRect = new Rect(); + + private RecentsView mRecentsView; + private View mClearAllButton; + + public RecentsViewContainer(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + mClearAllButton = findViewById(R.id.clear_all_button); + mClearAllButton.setOnClickListener((v) -> { + mRecentsView.dismissAllTasks(); + }); + + mRecentsView = (RecentsView) findViewById(R.id.overview_panel); + mRecentsView.setContainerView(this); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + + mRecentsView.getTaskSize(mTempRect); + + mClearAllButton.setTranslationX( + (mClearAllButton.getMeasuredWidth() - getResources().getDimension( + R.dimen.clear_all_container_width)) / 2); + mClearAllButton.setTranslationY( + mTempRect.top + (mTempRect.height() - mClearAllButton.getMeasuredHeight()) / 2 + - mClearAllButton.getY()); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + super.onTouchEvent(ev); + // Do not let touch escape to siblings below this view. This prevents scrolling of the + // workspace while in Recents. + return true; + } + + public void setContentAlpha(float alpha) { + if (alpha == mRecentsView.getContentAlpha()) { + return; + } + mRecentsView.setContentAlpha(alpha); + setVisibility(alpha > 0 ? VISIBLE : GONE); + } + + public void onEmptyStateChanged(boolean isEmpty) { + mClearAllButton.setVisibility(isEmpty ? GONE : VISIBLE); + } +} \ No newline at end of file diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml index a4acf06be5..6556adffc8 100644 --- a/res/layout/launcher.xml +++ b/res/layout/launcher.xml @@ -40,7 +40,7 @@ launcher:pageIndicator="@+id/page_indicator" /> diff --git a/res/values/config.xml b/res/values/config.xml index a40afe1138..9d1bb4110a 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -147,6 +147,7 @@ + 6 12 diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 88408606f5..ec0a8ffb3f 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -18,6 +18,7 @@ package com.android.launcher3; import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; + import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; @@ -205,6 +206,8 @@ public class Launcher extends BaseDraggingActivity // UI and state for the overview panel private View mOverviewPanel; + private View mOverviewPanelContainer; + @Thunk boolean mWorkspaceLoading = true; private OnResumeCallback mOnResumeCallback; @@ -912,6 +915,7 @@ public class Launcher extends BaseDraggingActivity mWorkspace = mDragLayer.findViewById(R.id.workspace); mWorkspace.initParentViews(mDragLayer); mOverviewPanel = findViewById(R.id.overview_panel); + mOverviewPanelContainer = findViewById(R.id.overview_panel_container); mHotseat = findViewById(R.id.hotseat); mDragHandleIndicator = findViewById(R.id.drag_indicator); mHotseatSearchBox = findViewById(R.id.search_container_hotseat); @@ -1192,6 +1196,10 @@ public class Launcher extends BaseDraggingActivity return (T) mOverviewPanel; } + public T getOverviewPanelContainer() { + return (T) mOverviewPanelContainer; + } + public DropTargetBar getDropTargetBar() { return mDropTargetBar; }