Introducing Clear-all button on Overview
It’s an initial implementation, i.e. no fancy effects.
It shares a parent (LauncherRecentsViewContainer) with RecentsView.
The button is centered in clear_all_button_container, which gets
positioned programmatically to the right of the last task. (RTL polish
will be a separate CL as well).
Bug: 72222505
Change-Id: Ia912908a93a30c2f51450ccf0f97c7495e7916d5
Test: Manual
diff --git a/quickstep/res/layout/fallback_recents_activity.xml b/quickstep/res/layout/fallback_recents_activity.xml
index 22f8b55..7ecab32 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">
- <com.android.quickstep.fallback.FallbackRecentsView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/overview_panel"
+ <com.android.quickstep.views.RecentsViewContainer
+ android:id="@+id/overview_panel_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:theme="@style/HomeScreenElementTheme" />
+ >
+ <include layout="@layout/overview_clear_all_button"/>
-</com.android.quickstep.fallback.RecentsRootView>
\ No newline at end of file
+ <com.android.quickstep.fallback.FallbackRecentsView
+ android:id="@id/overview_panel"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:focusableInTouchMode="true"
+ android:theme="@style/HomeScreenElementTheme"
+ >
+
+ </com.android.quickstep.fallback.FallbackRecentsView>
+ </com.android.quickstep.views.RecentsViewContainer>
+</com.android.quickstep.fallback.RecentsRootView>
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 0000000..8632f8b
--- /dev/null
+++ b/quickstep/res/layout/overview_clear_all_button.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/clear_all_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end|top"
+ android:fontFamily="sans-serif-medium"
+ android:text="@string/recents_clear_all"
+ android:textColor="?attr/workspaceTextColor"
+ android:background="?android:attr/selectableItemBackground"
+ android:textSize="14sp"
+/>
\ No newline at end of file
diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml
index 89e0571..6102c38 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.
-->
-<com.android.quickstep.views.LauncherRecentsView
+<com.android.quickstep.views.RecentsViewContainer
xmlns:android="http://schemas.android.com/apk/res/android"
- android:theme="@style/HomeScreenElementTheme"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:clipChildren="false"
- android:clipToPadding="false"
android:visibility="invisible"
- android:focusableInTouchMode="true" >
+>
+ <include layout="@layout/overview_clear_all_button"/>
-</com.android.quickstep.views.LauncherRecentsView>
\ No newline at end of file
+ <com.android.quickstep.views.LauncherRecentsView
+ android:id="@id/overview_panel"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:focusableInTouchMode="true"
+ android:theme="@style/HomeScreenElementTheme"
+ >
+
+ </com.android.quickstep.views.LauncherRecentsView>
+</com.android.quickstep.views.RecentsViewContainer>
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index c741913..0199cd9 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -45,4 +45,7 @@
<!-- Copied from framework resource:
docked_stack_divider_thickness - 2 * docked_stack_divider_insets -->
<dimen name="multi_window_task_divider_size">10dp</dimen>
+
+ <!-- Width of the space behind the last task in Overview. In the center of it, there is "Clear all" button. -->
+ <dimen name="clear_all_container_width">168dp</dimen>
</resources>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index bafa294..34cc0b7 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -35,4 +35,7 @@
<!-- Content description for the recent apps's accessibility option that closes it. [CHAR LIMIT=NONE] -->
<string name="accessibility_close_task">Close</string>
+
+ <!-- Recents: Title of a button that clears the task list, i.e. closes all tasks. [CHAR LIMIT=30] -->
+ <string name="recents_clear_all">Clear all</string>
</resources>
\ 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 124ec20..49d4931 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.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.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 @@
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 597e333..c602392 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -87,21 +87,10 @@
public abstract class RecentsView<T extends BaseActivity>
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<RecentsView> CONTENT_ALPHA =
- new FloatProperty<RecentsView>("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<RecentsView> ADJACENT_SCALE =
new FloatProperty<RecentsView>("adjacentScale") {
@Override
@@ -180,6 +169,8 @@
// Keeps track of task views whose visual state should not be reset
private ArraySet<TaskView> mIgnoreResetTaskViews = new ArraySet<>();
+ private RecentsViewContainer mContainerView;
+
// Variables for empty state
private final Drawable mEmptyIcon;
private final CharSequence mEmptyMessage;
@@ -320,12 +311,18 @@
@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 @@
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 @@
unloadVisibleTaskData();
setCurrentPage(0);
+ scrollTo(0, 0);
OverviewCallbacks.get(getContext()).onResetOverview();
}
@@ -844,11 +846,11 @@
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 @@
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 @@
mShowEmptyMessage = isEmpty;
updateEmptyStateUi(hasSizeChanged);
invalidate();
+ if (mContainerView != null) {
+ mContainerView.onEmptyStateChanged(!DEBUG_SHOW_CLEAR_ALL_BUTTON || mShowEmptyMessage);
+ }
}
@Override
@@ -1095,4 +1098,30 @@
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 0000000..cd4a6ff
--- /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<RecentsViewContainer> CONTENT_ALPHA =
+ new FloatProperty<RecentsViewContainer>("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 a4acf06..6556adf 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -40,7 +40,7 @@
launcher:pageIndicator="@+id/page_indicator" />
<include
- android:id="@+id/overview_panel"
+ android:id="@+id/overview_panel_container"
layout="@layout/overview_panel"
android:visibility="gone" />
diff --git a/res/values/config.xml b/res/values/config.xml
index a40afe1..9d1bb41 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -147,6 +147,7 @@
<item type="id" name="search_container_all_apps" />
<!-- Recents -->
+ <item type="id" name="overview_panel"/>
<integer name="config_recentsMaxThumbnailCacheSize">6</integer>
<integer name="config_recentsMaxIconCacheSize">12</integer>
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 8840860..ec0a8ff 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -18,6 +18,7 @@
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 @@
// UI and state for the overview panel
private View mOverviewPanel;
+ private View mOverviewPanelContainer;
+
@Thunk boolean mWorkspaceLoading = true;
private OnResumeCallback mOnResumeCallback;
@@ -912,6 +915,7 @@
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 @@
return (T) mOverviewPanel;
}
+ public <T extends View> T getOverviewPanelContainer() {
+ return (T) mOverviewPanelContainer;
+ }
+
public DropTargetBar getDropTargetBar() {
return mDropTargetBar;
}