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
This commit is contained in:
Vadim Tryshev 2018-04-11 17:19:08 -07:00
parent de967a2355
commit 6d2321cb7d
11 changed files with 201 additions and 39 deletions

View File

@ -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>
<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>

View File

@ -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"
/>

View File

@ -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>
<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>

View File

@ -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>

View File

@ -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>

View File

@ -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) {

View File

@ -87,21 +87,10 @@ import java.util.ArrayList;
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 @@ public abstract class RecentsView<T extends BaseActivity>
// 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 @@ public abstract class RecentsView<T extends BaseActivity>
@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<T extends BaseActivity>
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<T extends BaseActivity>
unloadVisibleTaskData();
setCurrentPage(0);
scrollTo(0, 0);
OverviewCallbacks.get(getContext()).onResetOverview();
}
@ -844,11 +846,11 @@ public abstract class RecentsView<T extends BaseActivity>
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<T extends BaseActivity>
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<T extends BaseActivity>
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<T extends BaseActivity>
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);
}
}

View File

@ -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);
}
}

View File

@ -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" />

View File

@ -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>

View File

@ -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 extends View> T getOverviewPanelContainer() {
return (T) mOverviewPanelContainer;
}
public DropTargetBar getDropTargetBar() {
return mDropTargetBar;
}