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

Commit 8573ff04 authored by Kevin's avatar Kevin
Browse files

Move clear all to recycler view (1/2)

First part of moving clear all button to recycler view.  This CL adds
support in the recycler view adapter for a clear all holder type and
hooks it up to the previous clear all animation.

Adding this breaks several assumptions made externally on the type of
the item and index which will be addressed in the second part.

Bug: 114136250
Test: Builds, testing pending 2nd part
Change-Id: Ib16790028d4e9f520945a987b3dace40d19f2468
parent 717a0370
Loading
Loading
Loading
Loading
+28 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
     Copyright (C) 2019 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->
<Button 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="center_horizontal"
    android:background="@drawable/clear_all_button"
    android:gravity="center"
    android:text="@string/recents_clear_all"
    android:textAllCaps="false"
    android:textColor="@color/clear_all_button_text"
    android:textSize="14sp">
</Button>
+3 −21
Original line number Diff line number Diff line
@@ -19,29 +19,11 @@
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <LinearLayout
        android:id="@+id/recent_task_content_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:visibility="gone">
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recent_task_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="none"/>
        <Button
            android:id="@+id/clear_all_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:background="@drawable/clear_all_button"
            android:gravity="center"
            android:text="@string/recents_clear_all"
            android:textAllCaps="false"
            android:textColor="@color/clear_all_button_text"
            android:textSize="14sp"/>
    </LinearLayout>
    <TextView
        android:id="@+id/recent_task_empty_view"
        android:layout_width="match_parent"
+30 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.quickstep;

import android.view.View;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;

/**
 * Holder for clear all button view in task recycler view.
 */
final class ClearAllHolder extends ViewHolder {
    public ClearAllHolder(@NonNull View itemView) {
        super(itemView);
    }
}
+75 −35
Original line number Diff line number Diff line
@@ -16,12 +16,14 @@
package com.android.quickstep;

import android.view.LayoutInflater;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView.Adapter;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;

import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.quickstep.views.TaskItemView;
import com.android.systemui.shared.recents.model.Task;
@@ -34,14 +36,17 @@ import java.util.Optional;
 * Recycler view adapter that dynamically inflates and binds {@link TaskHolder} instances with the
 * appropriate {@link Task} from the recents task list.
 */
public final class TaskAdapter extends Adapter<TaskHolder> {
public final class TaskAdapter extends Adapter<ViewHolder> {

    public static final int CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT = 0;
    public static final int MAX_TASKS_TO_DISPLAY = 6;

    private static final String TAG = "TaskAdapter";
    private static final int ITEM_TYPE_TASK = 0;
    private static final int ITEM_TYPE_CLEAR_ALL = 1;
    private final TaskListLoader mLoader;
    private TaskActionController mTaskActionController;
    private OnClickListener mClearAllListener;
    private boolean mIsShowingLoadingUi;

    public TaskAdapter(@NonNull TaskListLoader loader) {
@@ -52,6 +57,10 @@ public final class TaskAdapter extends Adapter<TaskHolder> {
        mTaskActionController = taskActionController;
    }

    public void setOnClearAllClickListener(OnClickListener listener) {
        mClearAllListener = listener;
    }

    /**
     * Sets all positions in the task adapter to loading views, binding new views if necessary.
     * This changes the task adapter's view of the data, so the appropriate notify events should be
@@ -65,21 +74,32 @@ public final class TaskAdapter extends Adapter<TaskHolder> {
    }

    @Override
    public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        switch (viewType) {
            case ITEM_TYPE_TASK:
                TaskItemView itemView = (TaskItemView) LayoutInflater.from(parent.getContext())
                        .inflate(R.layout.task_item_view, parent, false);
        TaskHolder holder = new TaskHolder(itemView);
        itemView.setOnClickListener(view -> mTaskActionController.launchTask(holder));
        return holder;
                TaskHolder taskHolder = new TaskHolder(itemView);
                itemView.setOnClickListener(view -> mTaskActionController.launchTask(taskHolder));
                return taskHolder;
            case ITEM_TYPE_CLEAR_ALL:
                Button clearView = (Button) LayoutInflater.from(parent.getContext())
                        .inflate(R.layout.clear_all_button, parent, false);
                ClearAllHolder clearAllHolder = new ClearAllHolder(clearView);
                clearView.setOnClickListener(mClearAllListener);
                return clearAllHolder;
            default:
                throw new IllegalArgumentException("No known holder for item type: " + viewType);
        }
    }

    @Override
    public void onBindViewHolder(TaskHolder holder, int position) {
    public void onBindViewHolder(ViewHolder holder, int position) {
        onBindViewHolderInternal(holder, position, false /* willAnimate */);
    }

    @Override
    public void onBindViewHolder(@NonNull TaskHolder holder, int position,
    public void onBindViewHolder(@NonNull ViewHolder holder, int position,
            @NonNull List<Object> payloads) {
        if (payloads.isEmpty()) {
            super.onBindViewHolder(holder, position, payloads);
@@ -95,10 +115,14 @@ public final class TaskAdapter extends Adapter<TaskHolder> {
        }
    }

    private void onBindViewHolderInternal(@NonNull TaskHolder holder, int position,
    private void onBindViewHolderInternal(@NonNull ViewHolder holder, int position,
            boolean willAnimate) {
        int itemType = getItemViewType(position);
        switch (itemType) {
            case ITEM_TYPE_TASK:
                TaskHolder taskHolder = (TaskHolder) holder;
                if (mIsShowingLoadingUi) {
            holder.bindEmptyUi();
                    taskHolder.bindEmptyUi();
                    return;
                }
                List<Task> tasks = mLoader.getCurrentTaskList();
@@ -107,28 +131,44 @@ public final class TaskAdapter extends Adapter<TaskHolder> {
                    return;
                }
                Task task = tasks.get(position);
        holder.bindTask(task, willAnimate /* willAnimate */);
                taskHolder.bindTask(task, willAnimate /* willAnimate */);
                mLoader.loadTaskIconAndLabel(task, () -> {
                    // Ensure holder still has the same task.
            if (Objects.equals(Optional.of(task), holder.getTask())) {
                holder.getTaskItemView().setIcon(task.icon);
                holder.getTaskItemView().setLabel(task.titleDescription);
                    if (Objects.equals(Optional.of(task), taskHolder.getTask())) {
                        taskHolder.getTaskItemView().setIcon(task.icon);
                        taskHolder.getTaskItemView().setLabel(task.titleDescription);
                    }
                });
                mLoader.loadTaskThumbnail(task, () -> {
            if (Objects.equals(Optional.of(task), holder.getTask())) {
                holder.getTaskItemView().setThumbnail(task.thumbnail.thumbnail);
                    if (Objects.equals(Optional.of(task), taskHolder.getTask())) {
                        taskHolder.getTaskItemView().setThumbnail(task.thumbnail.thumbnail);
                    }
                });
                break;
            case ITEM_TYPE_CLEAR_ALL:
                // Nothing to bind.
                break;
            default:
                throw new IllegalArgumentException("No known holder for item type: " + itemType);
        }
    }

    @Override
    public int getItemViewType(int position) {
        // Bottom is always clear all button.
        return (position == 0) ? ITEM_TYPE_CLEAR_ALL : ITEM_TYPE_TASK;
    }

    @Override
    public int getItemCount() {
        // Always at least one for clear all button.
        int itemCount = 1;
        if (mIsShowingLoadingUi) {
            // Show loading version of all items.
            return MAX_TASKS_TO_DISPLAY;
            itemCount += MAX_TASKS_TO_DISPLAY;
        } else {
            return Math.min(mLoader.getCurrentTaskList().size(), MAX_TASKS_TO_DISPLAY);
            itemCount += Math.min(mLoader.getCurrentTaskList().size(), MAX_TASKS_TO_DISPLAY);
        }
        return itemCount;
    }
}
+4 −15
Original line number Diff line number Diff line
@@ -18,9 +18,6 @@ package com.android.quickstep.views;
import static androidx.recyclerview.widget.LinearLayoutManager.VERTICAL;

import static com.android.quickstep.TaskAdapter.CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT;
import static com.android.quickstep.views.TaskLayoutUtils.getClearAllButtonHeight;
import static com.android.quickstep.views.TaskLayoutUtils.getClearAllButtonTopBottomMargin;
import static com.android.quickstep.views.TaskLayoutUtils.getClearAllButtonWidth;
import static com.android.quickstep.views.TaskLayoutUtils.getTaskListHeight;

import android.animation.Animator;
@@ -114,7 +111,6 @@ public final class IconRecentsView extends FrameLayout {
    private View mShowingContentView;
    private View mEmptyView;
    private View mContentView;
    private View mClearAllView;
    private boolean mTransitionedFromApp;
    private AnimatorSet mLayoutAnimation;
    private final ArraySet<View> mLayingOutViews = new ArraySet<>();
@@ -141,6 +137,7 @@ public final class IconRecentsView extends FrameLayout {
        mDeviceProfile = activity.getDeviceProfile();
        mTaskLoader = new TaskListLoader(mContext);
        mTaskAdapter = new TaskAdapter(mTaskLoader);
        mTaskAdapter.setOnClearAllClickListener(view -> animateClearAllTasks());
        mTaskActionController = new TaskActionController(mTaskLoader, mTaskAdapter);
        mTaskAdapter.setActionController(mTaskActionController);
        RecentsModel.INSTANCE.get(context).addThumbnailChangeListener(listener);
@@ -178,7 +175,7 @@ public final class IconRecentsView extends FrameLayout {
                    () -> mTaskRecyclerView.setItemAnimator(new DefaultItemAnimator()));

            mEmptyView = findViewById(R.id.recent_task_empty_view);
            mContentView = findViewById(R.id.recent_task_content_view);
            mContentView = mTaskRecyclerView;
            mTaskAdapter.registerAdapterDataObserver(new AdapterDataObserver() {
                @Override
                public void onChanged() {
@@ -190,16 +187,7 @@ public final class IconRecentsView extends FrameLayout {
                    updateContentViewVisibility();
                }
            });
            // TODO: Move clear all button to recycler view so that it can scroll off screen.
            // TODO: Move layout param logic into onMeasure
            mClearAllView = findViewById(R.id.clear_all_button);
            MarginLayoutParams clearAllParams =
                    (MarginLayoutParams) mClearAllView.getLayoutParams();
            clearAllParams.height = getClearAllButtonHeight(mDeviceProfile);
            clearAllParams.width = getClearAllButtonWidth(mDeviceProfile);
            clearAllParams.topMargin = getClearAllButtonTopBottomMargin(mDeviceProfile);
            clearAllParams.bottomMargin = getClearAllButtonTopBottomMargin(mDeviceProfile);
            mClearAllView.setOnClickListener(v -> animateClearAllTasks());
        }
    }

@@ -210,7 +198,7 @@ public final class IconRecentsView extends FrameLayout {
        for (TaskItemView itemView : itemViews) {
            itemView.setEnabled(enabled);
        }
        mClearAllView.setEnabled(enabled);
        // TODO: Disable clear all button.
    }

    /**
@@ -365,6 +353,7 @@ public final class IconRecentsView extends FrameLayout {
     * @return array of attached task item views
     */
    private TaskItemView[] getTaskViews() {
        // TODO: Check that clear all button isn't here..
        int taskCount = mTaskRecyclerView.getChildCount();
        TaskItemView[] itemViews = new TaskItemView[taskCount];
        for (int i = 0; i < taskCount; i ++) {