From 327e01d98c869a83a9c80cdab32f1add3f87713b Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 26 Feb 2019 13:05:31 -0800 Subject: [PATCH 01/44] Fixing task snapshots not getting updated in recents view Bug: 124620962 Change-Id: I7c6b9d1183faa01a0836194e42a9cd8517c8b61a --- .../android/quickstep/views/RecentsView.java | 35 +++++----- .../com/android/quickstep/views/TaskView.java | 25 ++++--- .../com/android/quickstep/RecentsModel.java | 42 ++++++++---- .../android/quickstep/TaskThumbnailCache.java | 66 ++++++++++++++----- 4 files changed, 113 insertions(+), 55 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 8faf95d5b7..a7bf2c3ee4 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -90,6 +90,7 @@ import com.android.launcher3.util.ViewPool; import com.android.quickstep.OverviewCallbacks; import com.android.quickstep.RecentsAnimationWrapper; import com.android.quickstep.RecentsModel; +import com.android.quickstep.RecentsModel.TaskThumbnailChangeListener; import com.android.quickstep.TaskThumbnailCache; import com.android.quickstep.TaskUtils; import com.android.quickstep.util.ClipAnimationHelper; @@ -113,7 +114,7 @@ import java.util.function.Consumer; @TargetApi(Build.VERSION_CODES.P) public abstract class RecentsView extends PagedView implements Insettable, TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback, - InvariantDeviceProfile.OnIDPChangeListener { + InvariantDeviceProfile.OnIDPChangeListener, TaskThumbnailChangeListener { private static final String TAG = RecentsView.class.getSimpleName(); @@ -170,14 +171,6 @@ public abstract class RecentsView extends PagedView impl * TODO: Call reloadIdNeeded in onTaskStackChanged. */ private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() { - @Override - public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) { - if (!mHandleTaskStackChanges) { - return; - } - updateThumbnail(taskId, snapshot); - } - @Override public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { if (!mHandleTaskStackChanges) { @@ -262,7 +255,6 @@ public abstract class RecentsView extends PagedView impl private boolean mOverviewStateEnabled; private boolean mHandleTaskStackChanges; - private Runnable mNextPageSwitchRunnable; private boolean mSwipeDownShouldLaunchApp; private boolean mTouchDownToStartHome; private final int mTouchSlop; @@ -340,6 +332,19 @@ public abstract class RecentsView extends PagedView impl return mIsRtl; } + @Override + public Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) { + if (mHandleTaskStackChanges) { + TaskView taskView = getTaskView(taskId); + if (taskView != null) { + Task task = taskView.getTask(); + taskView.getThumbnail().setThumbnail(task, thumbnailData); + return task; + } + } + return null; + } + public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData) { TaskView taskView = getTaskView(taskId); if (taskView != null) { @@ -371,6 +376,7 @@ public abstract class RecentsView extends PagedView impl mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener); ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); mSyncTransactionApplier = new SyncRtSurfaceTransactionApplierCompat(this); + RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this); mIdp.addOnChangeListener(this); } @@ -382,6 +388,7 @@ public abstract class RecentsView extends PagedView impl mActivity.removeMultiWindowModeChangedListener(mMultiWindowModeChangedListener); ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener); mSyncTransactionApplier = null; + RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this); mIdp.removeOnChangeListener(this); } @@ -421,17 +428,9 @@ public abstract class RecentsView extends PagedView impl updateTaskStackListenerState(); } - public void setNextPageSwitchRunnable(Runnable r) { - mNextPageSwitchRunnable = r; - } - @Override protected void onPageEndTransition() { super.onPageEndTransition(); - if (mNextPageSwitchRunnable != null) { - mNextPageSwitchRunnable.run(); - mNextPageSwitchRunnable = null; - } if (getNextPage() > 0) { setSwipeDownShouldLaunchApp(true); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java index 942214152f..fb58c243b5 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java @@ -29,7 +29,6 @@ import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; import android.app.ActivityOptions; import android.content.Context; -import android.content.Intent; import android.content.res.Resources; import android.graphics.Outline; import android.graphics.drawable.Drawable; @@ -215,6 +214,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { * Updates this task view to the given {@param task}. */ public void bind(Task task) { + cancelPendingLoadTasks(); mTask = task; mSnapshotView.bind(task); } @@ -305,15 +305,15 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { if (mTask == null) { return; } + cancelPendingLoadTasks(); if (visible) { // These calls are no-ops if the data is already loaded, try and load the high // resolution thumbnail if the state permits RecentsModel model = RecentsModel.INSTANCE.get(getContext()); TaskThumbnailCache thumbnailCache = model.getThumbnailCache(); TaskIconCache iconCache = model.getIconCache(); - mThumbnailLoadRequest = thumbnailCache.updateThumbnailInBackground(mTask, - !thumbnailCache.getHighResLoadingState().isEnabled() /* reducedResolution */, - (task) -> mSnapshotView.setThumbnail(task, task.thumbnail)); + mThumbnailLoadRequest = thumbnailCache.updateThumbnailInBackground( + mTask, thumbnail -> mSnapshotView.setThumbnail(mTask, thumbnail)); mIconLoadRequest = iconCache.updateIconInBackground(mTask, (task) -> { setIcon(task.icon); @@ -325,17 +325,22 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { }); }); } else { - if (mThumbnailLoadRequest != null) { - mThumbnailLoadRequest.cancel(); - } - if (mIconLoadRequest != null) { - mIconLoadRequest.cancel(); - } mSnapshotView.setThumbnail(null, null); setIcon(null); } } + private void cancelPendingLoadTasks() { + if (mThumbnailLoadRequest != null) { + mThumbnailLoadRequest.cancel(); + mThumbnailLoadRequest = null; + } + if (mIconLoadRequest != null) { + mIconLoadRequest.cancel(); + mIconLoadRequest = null; + } + } + private boolean showTaskMenu() { getRecentsView().snapToPage(getRecentsView().indexOfChild(this)); mMenuView = TaskMenuView.showForTask(this); diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java index f3e1545e0b..81a22a190a 100644 --- a/quickstep/src/com/android/quickstep/RecentsModel.java +++ b/quickstep/src/com/android/quickstep/RecentsModel.java @@ -15,33 +15,29 @@ */ package com.android.quickstep; +import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId; + import android.annotation.TargetApi; import android.app.ActivityManager; import android.content.ComponentCallbacks2; import android.content.Context; import android.os.Build; -import android.os.Bundle; import android.os.HandlerThread; import android.os.Process; import android.os.RemoteException; import android.util.Log; -import android.util.SparseArray; -import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.util.MainThreadInitializedObject; -import com.android.launcher3.util.Preconditions; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListener; import java.util.ArrayList; +import java.util.List; import java.util.function.Consumer; -import androidx.annotation.WorkerThread; - -import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId; - /** * Singleton class to load and manage recents model. */ @@ -54,8 +50,8 @@ public class RecentsModel extends TaskStackChangeListener { public static final MainThreadInitializedObject INSTANCE = new MainThreadInitializedObject<>(c -> new RecentsModel(c)); + private final List mThumbnailChangeListeners = new ArrayList<>(); private final Context mContext; - private final MainThreadExecutor mMainThreadExecutor; private ISystemUiProxy mSystemUiProxy; @@ -68,9 +64,6 @@ public class RecentsModel extends TaskStackChangeListener { private RecentsModel(Context context) { mContext = context; - - mMainThreadExecutor = new MainThreadExecutor(); - HandlerThread loaderThread = new HandlerThread("TaskThumbnailIconCache", Process.THREAD_PRIORITY_BACKGROUND); loaderThread.start(); @@ -168,6 +161,18 @@ public class RecentsModel extends TaskStackChangeListener { }); } + @Override + public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) { + mThumbnailCache.updateTaskSnapShot(taskId, snapshot); + + for (int i = mThumbnailChangeListeners.size() - 1; i >= 0; i--) { + Task task = mThumbnailChangeListeners.get(i).onTaskThumbnailChanged(taskId, snapshot); + if (task != null) { + task.thumbnail = snapshot; + } + } + } + public void setSystemUiProxy(ISystemUiProxy systemUiProxy) { mSystemUiProxy = systemUiProxy; } @@ -239,4 +244,17 @@ public class RecentsModel extends TaskStackChangeListener { + ": ", e); } } + + public void addThumbnailChangeListener(TaskThumbnailChangeListener listener) { + mThumbnailChangeListeners.add(listener); + } + + public void removeThumbnailChangeListener(TaskThumbnailChangeListener listener) { + mThumbnailChangeListeners.remove(listener); + } + + public interface TaskThumbnailChangeListener { + + Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData); + } } diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java index 7a216ed2f5..d05196bc84 100644 --- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java +++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java @@ -26,6 +26,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.icons.cache.HandlerRunnable; import com.android.launcher3.util.Preconditions; import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.Task.TaskKey; import com.android.systemui.shared.recents.model.TaskKeyLruCache; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -38,7 +39,7 @@ public class TaskThumbnailCache { private final MainThreadExecutor mMainThreadExecutor; private final int mCacheSize; - private final TaskKeyLruCache mCache; + private final ThumbnailCache mCache; private final HighResLoadingState mHighResLoadingState; public static class HighResLoadingState { @@ -98,7 +99,7 @@ public class TaskThumbnailCache { Resources res = context.getResources(); mCacheSize = res.getInteger(R.integer.recentsThumbnailCacheSize); - mCache = new TaskKeyLruCache<>(mCacheSize); + mCache = new ThumbnailCache(mCacheSize); } /** @@ -106,13 +107,20 @@ public class TaskThumbnailCache { */ public void updateThumbnailInCache(Task task) { Preconditions.assertUIThread(); - // Fetch the thumbnail for this task and put it in the cache - updateThumbnailInBackground(task, true /* reducedResolution */, (t) -> { - mCache.put(task.key, t.thumbnail); - }); + if (task.thumbnail == null) { + updateThumbnailInBackground(task.key, true /* reducedResolution */, + t -> task.thumbnail = t); + } } + /** + * Synchronously updates the thumbnail in the cache if it is already there. + */ + public void updateTaskSnapShot(int taskId, ThumbnailData thumbnail) { + Preconditions.assertUIThread(); + mCache.updateIfAlreadyInCache(taskId, thumbnail); + } /** * Asynchronously fetches the icon and other task data for the given {@param task}. @@ -120,22 +128,33 @@ public class TaskThumbnailCache { * @param callback The callback to receive the task after its data has been populated. * @return A cancelable handle to the request */ - public ThumbnailLoadRequest updateThumbnailInBackground(Task task, boolean reducedResolution, - Consumer callback) { + public ThumbnailLoadRequest updateThumbnailInBackground( + Task task, Consumer callback) { Preconditions.assertUIThread(); + boolean reducedResolution = !mHighResLoadingState.isEnabled(); if (task.thumbnail != null && (!task.thumbnail.reducedResolution || reducedResolution)) { // Nothing to load, the thumbnail is already high-resolution or matches what the // request, so just callback - callback.accept(task); + callback.accept(task.thumbnail); return null; } - ThumbnailData cachedThumbnail = mCache.getAndInvalidateIfModified(task.key); + + return updateThumbnailInBackground(task.key, !mHighResLoadingState.isEnabled(), t -> { + task.thumbnail = t; + callback.accept(t); + }); + } + + private ThumbnailLoadRequest updateThumbnailInBackground(TaskKey key, boolean reducedResolution, + Consumer callback) { + Preconditions.assertUIThread(); + + ThumbnailData cachedThumbnail = mCache.getAndInvalidateIfModified(key); if (cachedThumbnail != null && (!cachedThumbnail.reducedResolution || reducedResolution)) { // Already cached, lets use that thumbnail - task.thumbnail = cachedThumbnail; - callback.accept(task); + callback.accept(cachedThumbnail); return null; } @@ -144,14 +163,14 @@ public class TaskThumbnailCache { @Override public void run() { ThumbnailData thumbnail = ActivityManagerWrapper.getInstance().getTaskThumbnail( - task.key.id, reducedResolution); + key.id, reducedResolution); if (isCanceled()) { // We don't call back to the provided callback in this case return; } mMainThreadExecutor.execute(() -> { - task.thumbnail = thumbnail; - callback.accept(task); + mCache.put(key, thumbnail); + callback.accept(thumbnail); onEnd(); }); } @@ -196,4 +215,21 @@ public class TaskThumbnailCache { this.reducedResolution = reducedResolution; } } + + private static class ThumbnailCache extends TaskKeyLruCache { + + public ThumbnailCache(int cacheSize) { + super(cacheSize); + } + + /** + * Updates the cache entry if it is already present in the cache + */ + public void updateIfAlreadyInCache(int taskId, ThumbnailData thumbnailData) { + ThumbnailData oldData = getCacheEntry(taskId); + if (oldData != null) { + putCacheEntry(taskId, thumbnailData); + } + } + } } From 0fcd22fc66ef544c2e082b7a5aa406edc78e22a9 Mon Sep 17 00:00:00 2001 From: Kevin Date: Mon, 25 Feb 2019 12:04:02 -0800 Subject: [PATCH 02/44] Add TaskAdapter to recents Go. This CL adds a recycler view adapter for tasks to manage the item control logic. The data source itself and view hierarchy is planned to be added in future CLs. Bug: 114136250 Test: Build l3GoIconRecents Change-Id: I72d4f9df68d17fd745947d36522cde342ea58317 --- .../com/android/quickstep/TaskAdapter.java | 58 +++++++++++++++++++ .../src/com/android/quickstep/TaskHolder.java | 48 +++++++++++++++ .../quickstep/views/IconRecentsView.java | 20 ++++++- 3 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 go/quickstep/src/com/android/quickstep/TaskAdapter.java create mode 100644 go/quickstep/src/com/android/quickstep/TaskHolder.java diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java new file mode 100644 index 0000000000..77c3f33b04 --- /dev/null +++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java @@ -0,0 +1,58 @@ +/* + * 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.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView.Adapter; + +import com.android.systemui.shared.recents.model.Task; + +import java.util.ArrayList; + +/** + * 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 { + + private static final int MAX_TASKS_TO_DISPLAY = 6; + private static final String TAG = "TaskAdapter"; + private final ArrayList mTaskList; + + public TaskAdapter(@NonNull ArrayList taskList) { + mTaskList = taskList; + } + + @Override + public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) { + // TODO: Swap in an actual task view here (view w/ icon, label, etc.) + TextView stubView = new TextView(parent.getContext()); + return new TaskHolder(stubView); + } + + @Override + public void onBindViewHolder(TaskHolder holder, int position) { + holder.bindTask(mTaskList.get(position)); + } + + @Override + public int getItemCount() { + return Math.min(mTaskList.size(), MAX_TASKS_TO_DISPLAY); + } +} diff --git a/go/quickstep/src/com/android/quickstep/TaskHolder.java b/go/quickstep/src/com/android/quickstep/TaskHolder.java new file mode 100644 index 0000000000..1ea6d7610f --- /dev/null +++ b/go/quickstep/src/com/android/quickstep/TaskHolder.java @@ -0,0 +1,48 @@ +/* + * 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.widget.TextView; + +import androidx.recyclerview.widget.RecyclerView.ViewHolder; + +import com.android.systemui.shared.recents.model.Task; + +/** + * A recycler view holder that holds the task view and binds {@link Task} content (app title, icon, + * etc.) to the view. + */ +final class TaskHolder extends ViewHolder { + + // TODO: Implement the actual task view to be held. + // For now, we just use a simple text view. + private final TextView mStubView; + + public TaskHolder(TextView stubView) { + super(stubView); + mStubView = stubView; + } + + /** + * Bind task content to the view. This includes the task icon and title as well as binding + * input handlers such as which task to launch/remove. + * + * @param task the task to bind to the view this + */ + public void bindTask(Task task) { + mStubView.setText("Stub task view: " + task.titleDescription); + } +} diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java index e4741e9d50..ae8166c236 100644 --- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java +++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java @@ -21,8 +21,14 @@ import android.util.FloatProperty; import android.view.ViewDebug; import android.widget.FrameLayout; +import com.android.quickstep.TaskAdapter; +import com.android.systemui.shared.recents.model.Task; + +import java.util.ArrayList; + /** - * Root view for the icon recents view. + * Root view for the icon recents view. Acts as the main interface to the rest of the Launcher code + * base. */ public final class IconRecentsView extends FrameLayout { @@ -58,12 +64,24 @@ public final class IconRecentsView extends FrameLayout { * is top aligned and 0.5 is centered vertically. */ @ViewDebug.ExportedProperty(category = "launcher") + + // TODO: Write a recents task list observer that creates/updates tasks and signals task adapter. + private static final ArrayList DUMMY_TASK_LIST = new ArrayList<>(); + private float mTranslationYFactor; + private TaskAdapter mTaskAdapter; public IconRecentsView(Context context, AttributeSet attrs) { super(context, attrs); } + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mTaskAdapter = new TaskAdapter(DUMMY_TASK_LIST); + // TODO: Hook task adapter up to recycler view. + } + public void setTranslationYFactor(float translationFactor) { mTranslationYFactor = translationFactor; setTranslationY(computeTranslationYForFactor(mTranslationYFactor)); From ff979d2d05d61f01332913881709cd38de8bdf96 Mon Sep 17 00:00:00 2001 From: Tracy Zhou Date: Tue, 26 Feb 2019 14:17:17 -0800 Subject: [PATCH 03/44] Fix rounded corner regression upon swipe up ag/5990051 introduced the regression. Change-Id: Ia34eba3d109b0f8bc87b46585dce85a9e86baf12 Fixes: 126045978 Test: Manual --- .../src/com/android/quickstep/util/ClipAnimationHelper.java | 1 + 1 file changed, 1 insertion(+) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java index 6d374c61bc..4450b4b982 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java @@ -185,6 +185,7 @@ public class ClipAnimationHelper { if (mSupportsRoundedCornersOnWindows) { cornerRadius = Utilities.mapRange(params.progress, mWindowCornerRadius, mTaskCornerRadius); + mCurrentCornerRadius = cornerRadius; } } alpha = mTaskAlphaCallback.apply(app, params.targetAlpha); From 34cf2616b58afdb4dd0b1760b9ea12b83b1e1187 Mon Sep 17 00:00:00 2001 From: Kevin Date: Tue, 26 Feb 2019 13:23:41 -0800 Subject: [PATCH 04/44] Fix issue keeping Go recents view GONE Go recents view was always GONE before. This is because Go was missing logic in its CONTENT_ALPHA property to set visibility on appropriate alpha changes. This CL fixes this. Bug: 114136250 Test: Manual test and see the view is there Change-Id: I45fea6d695b9b3154be9d2e6fae25ca8a8aa288e --- .../src/com/android/quickstep/views/IconRecentsView.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java index ae8166c236..00415fe1a0 100644 --- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java +++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java @@ -51,6 +51,11 @@ public final class IconRecentsView extends FrameLayout { @Override public void setValue(IconRecentsView view, float v) { ALPHA.set(view, v); + if (view.getVisibility() != VISIBLE && v > 0) { + view.setVisibility(VISIBLE); + } else if (view.getVisibility() != GONE && v == 0){ + view.setVisibility(GONE); + } } @Override From dd50f6a7dca9c884b257c6afa04a71f436552785 Mon Sep 17 00:00:00 2001 From: Kevin Date: Tue, 26 Feb 2019 17:08:49 -0800 Subject: [PATCH 05/44] Fix build break for Launcher3GoIconRecents The make rule for recents Go was called Launcher3QuickStepGoIconRecents in the make file while the one in the presubmit build rule is called Launcher3GoIconRecents. I opted to change the one in the make file since the one in presubmit is shorter. Test: Builds Change-Id: I740534707206d79d364312bfaf3a623a3c429f52 --- Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 628b632cc6..74b96d4edc 100644 --- a/Android.mk +++ b/Android.mk @@ -319,7 +319,7 @@ LOCAL_RESOURCE_DIR := \ LOCAL_PROGUARD_FLAG_FILES := proguard.flags LOCAL_PROGUARD_ENABLED := full -LOCAL_PACKAGE_NAME := Launcher3QuickStepGoIconRecents +LOCAL_PACKAGE_NAME := Launcher3GoIconRecents LOCAL_PRIVILEGED_MODULE := true LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3 From ceedc95d4c9feb1131d955d07a3274dd14728620 Mon Sep 17 00:00:00 2001 From: Vadim Tryshev Date: Thu, 30 Aug 2018 16:01:47 -0700 Subject: [PATCH 06/44] Slowing down dismiss gesture to reduce flakiness Change-Id: I13a1d2053e841994df537896c4fe397f68fa4a65 --- tests/tapl/com/android/launcher3/tapl/OverviewTask.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java index 48686c448a..7ccd49bd56 100644 --- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java +++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java @@ -24,6 +24,7 @@ import androidx.test.uiautomator.Until; * A recent task in the overview panel carousel. */ public final class OverviewTask { + static final int FLING_SPEED = 3000; private final LauncherInstrumentation mLauncher; private final UiObject2 mTask; private final BaseOverview mOverview; @@ -45,7 +46,7 @@ public final class OverviewTask { public void dismiss() { verifyActiveContainer(); // Dismiss the task via flinging it up. - mTask.fling(Direction.DOWN); + mTask.fling(Direction.DOWN, (int) (FLING_SPEED * mLauncher.getDisplayDensity())); mLauncher.waitForIdle(); } From 21950c52f8f85057e54635fd87f97f66a096c440 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 26 Feb 2019 17:22:23 -0800 Subject: [PATCH 07/44] Adding workaround to defer recents animation cancel Bug: 126336729 Test: Open gmail, quickly tap home Change-Id: I98575f0c949ad105c214c6a4a2338a67d1496972 --- .../quickstep/OtherActivityTouchConsumer.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityTouchConsumer.java index 77f900f690..012e6709cd 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityTouchConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityTouchConsumer.java @@ -36,6 +36,8 @@ import android.content.Intent; import android.graphics.PointF; import android.graphics.Rect; import android.os.Build; +import android.os.Handler; +import android.os.Looper; import android.view.Display; import android.view.MotionEvent; import android.view.Surface; @@ -105,6 +107,12 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC // TODO: Start displacement should have both x and y private float mStartDisplacement; + private Handler mMainThreadHandler; + private Runnable mCancelRecentsAnimationRunnable = () -> { + ActivityManagerWrapper.getInstance().cancelRecentsAnimation( + true /* restoreHomeStackPosition */); + }; + public OtherActivityTouchConsumer(Context base, RunningTaskInfo runningTaskInfo, RecentsModel recentsModel, Intent homeIntent, ActivityControlHelper activityControl, boolean isDeferredDownTarget, OverviewCallbacks overviewCallbacks, @@ -113,6 +121,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC SwipeSharedState swipeSharedState) { super(base); + mMainThreadHandler = new Handler(Looper.getMainLooper()); mRunningTask = runningTaskInfo; mRecentsModel = recentsModel; mHomeIntent = homeIntent; @@ -328,10 +337,12 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC onConsumerAboutToBeSwitched(); onInteractionGestureFinished(); - // Also clean up in case the system has handled the UP and canceled the animation before - // we had a chance to start the recents animation. In such a case, we will not receive - ActivityManagerWrapper.getInstance().cancelRecentsAnimation( - true /* restoreHomeStackPosition */); + // Cancel the recents animation if SysUI happens to handle UP before we have a chance + // to start the recents animation. In addition, workaround for b/126336729 by delaying + // the cancel of the animation for a period, in case SysUI is slow to handle UP and we + // handle DOWN & UP and move the home stack before SysUI can start the activity + mMainThreadHandler.removeCallbacks(mCancelRecentsAnimationRunnable); + mMainThreadHandler.postDelayed(mCancelRecentsAnimationRunnable, 100); } mVelocityTracker.recycle(); mVelocityTracker = null; @@ -341,6 +352,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC @Override public void onConsumerAboutToBeSwitched() { Preconditions.assertUIThread(); + mMainThreadHandler.removeCallbacks(mCancelRecentsAnimationRunnable); if (mInteractionHandler != null) { // The consumer is being switched while we are active. Set up the shared state to be // used by the next animation From 6ab083273940c796fd5ab4853bb9c55131aa33e6 Mon Sep 17 00:00:00 2001 From: Tracy Zhou Date: Thu, 21 Feb 2019 19:53:27 -0800 Subject: [PATCH 08/44] Handle key events in Launcher. Change-Id: I7531080a7534ba2788cebec723ce552609c92b1c Fixes: 125551024 Test: Swipe up from app and press back. It takes user back to the current app. --- quickstep/libs/sysui_shared.jar | Bin 153404 -> 155367 bytes ...{TouchConsumer.java => InputConsumer.java} | 22 ++++++--- ...r.java => OtherActivityInputConsumer.java} | 16 +++---- ...nsumer.java => OverviewInputConsumer.java} | 27 +++++++---- .../quickstep/RecentsAnimationWrapper.java | 43 ++++++++++++------ .../quickstep/TouchInteractionLog.java | 2 +- .../quickstep/TouchInteractionService.java | 20 ++++---- .../WindowTransformSwipeHandler.java | 9 ++-- .../StartLauncherViaGestureTests.java | 8 ++-- 9 files changed, 91 insertions(+), 56 deletions(-) rename quickstep/recents_ui_overrides/src/com/android/quickstep/{TouchConsumer.java => InputConsumer.java} (69%) rename quickstep/recents_ui_overrides/src/com/android/quickstep/{OtherActivityTouchConsumer.java => OtherActivityInputConsumer.java} (96%) rename quickstep/recents_ui_overrides/src/com/android/quickstep/{OverviewTouchConsumer.java => OverviewInputConsumer.java} (86%) diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar index 5ec699cd01a67d1c8716bd5cba59e61b5c88cce2..8c58e3ed5970f5fdd0bef90599f4b837c8272be6 100644 GIT binary patch delta 16521 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 S!uwZS5CY;0`Fk!i#Qy^+mKIZ}Tf-n7~{) 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 { - - TouchConsumer NO_OP = (ev) -> {}; +public interface InputConsumer { + InputConsumer NO_OP = new InputConsumer() { }; default boolean isActive() { return false; @@ -35,4 +33,16 @@ public interface TouchConsumer extends Consumer { * Called by the event queue when the consumer is about to be switched to a new consumer. */ default void onConsumerAboutToBeSwitched() { } + + default void onMotionEvent(MotionEvent ev) { } + + default void onKeyEvent(KeyEvent ev) { } + + default void onInputEvent(InputEvent ev) { + if (ev instanceof MotionEvent) { + onMotionEvent((MotionEvent) ev); + } else { + onKeyEvent((KeyEvent) ev); + } + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java similarity index 96% rename from quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityTouchConsumer.java rename to quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java index 012e6709cd..67bfeaa24e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityTouchConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java @@ -64,13 +64,13 @@ import com.android.systemui.shared.system.WindowManagerWrapper; import java.util.function.Consumer; /** - * Touch consumer for handling events originating from an activity other than Launcher + * Input consumer for handling events originating from an activity other than Launcher */ @TargetApi(Build.VERSION_CODES.P) -public class OtherActivityTouchConsumer extends ContextWrapper implements TouchConsumer { +public class OtherActivityInputConsumer extends ContextWrapper implements InputConsumer { - public static final String DOWN_EVT = "OtherActivityTouchConsumer.DOWN"; - private static final String UP_EVT = "OtherActivityTouchConsumer.UP"; + public static final String DOWN_EVT = "OtherActivityInputConsumer.DOWN"; + private static final String UP_EVT = "OtherActivityInputConsumer.UP"; private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher(); private final RunningTaskInfo mRunningTask; @@ -85,7 +85,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC private final int mDisplayRotation; private final Rect mStableInsets = new Rect(); - private final Consumer mOnCompleteCallback; + private final Consumer mOnCompleteCallback; private final MotionPauseDetector mMotionPauseDetector; private VelocityTracker mVelocityTracker; @@ -113,11 +113,11 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC true /* restoreHomeStackPosition */); }; - public OtherActivityTouchConsumer(Context base, RunningTaskInfo runningTaskInfo, + public OtherActivityInputConsumer(Context base, RunningTaskInfo runningTaskInfo, RecentsModel recentsModel, Intent homeIntent, ActivityControlHelper activityControl, boolean isDeferredDownTarget, OverviewCallbacks overviewCallbacks, TaskOverlayFactory taskOverlayFactory, InputConsumerController inputConsumer, - Consumer onCompleteCallback, + Consumer onCompleteCallback, SwipeSharedState swipeSharedState) { super(base); @@ -148,7 +148,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC } @Override - public void accept(MotionEvent ev) { + public void onMotionEvent(MotionEvent ev) { if (mVelocityTracker == null) { return; } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java similarity index 86% rename from quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewTouchConsumer.java rename to quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java index 4da52e15ca..28c4db46a3 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewTouchConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java @@ -20,10 +20,12 @@ import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_UP; +import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; import android.graphics.PointF; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.ViewConfiguration; @@ -33,10 +35,10 @@ import com.android.quickstep.util.CachedEventDispatcher; import com.android.systemui.shared.system.ActivityManagerWrapper; /** - * Touch consumer for handling touch on the recents/Launcher activity. + * Input consumer for handling touch on the recents/Launcher activity. */ -public class OverviewTouchConsumer - implements TouchConsumer { +public class OverviewInputConsumer + implements InputConsumer { private final CachedEventDispatcher mCachedEventDispatcher = new CachedEventDispatcher(); private final T mActivity; @@ -50,7 +52,7 @@ public class OverviewTouchConsumer private boolean mTrackingStarted = false; private boolean mInvalidated = false; - OverviewTouchConsumer(T activity, boolean startingInActivityBounds) { + OverviewInputConsumer(T activity, boolean startingInActivityBounds) { mActivity = activity; mTarget = activity.getDragLayer(); mTouchSlop = ViewConfiguration.get(mActivity).getScaledTouchSlop(); @@ -58,7 +60,7 @@ public class OverviewTouchConsumer } @Override - public void accept(MotionEvent ev) { + public void onMotionEvent(MotionEvent ev) { if (mInvalidated) { return; } @@ -96,11 +98,18 @@ public class OverviewTouchConsumer // Set an empty consumer to that all the cached events are cleared if (!mCachedEventDispatcher.hasConsumer()) { - mCachedEventDispatcher.setConsumer(NO_OP); + mCachedEventDispatcher.setConsumer(motionEvent -> { }); } } } + @Override + public void onKeyEvent(KeyEvent ev) { + if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { + mActivity.dispatchKeyEvent(ev); + } + } + private void startTouchTracking(MotionEvent ev, boolean updateLocationOffset, boolean closeActiveWindows) { if (updateLocationOffset) { @@ -135,12 +144,12 @@ public class OverviewTouchConsumer ev.setEdgeFlags(flags); } - public static TouchConsumer newInstance(ActivityControlHelper activityHelper, + public static InputConsumer newInstance(ActivityControlHelper activityHelper, boolean startingInActivityBounds) { BaseDraggingActivity activity = activityHelper.getCreatedActivity(); if (activity == null) { - return TouchConsumer.NO_OP; + return InputConsumer.NO_OP; } - return new OverviewTouchConsumer(activity, startingInActivityBounds); + return new OverviewInputConsumer(activity, startingInActivityBounds); } } \ No newline at end of file diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java index 5e7c1a1c57..f0bc223ded 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java @@ -19,6 +19,8 @@ import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_UP; +import android.view.InputEvent; +import android.view.KeyEvent; import android.view.MotionEvent; import com.android.launcher3.util.Preconditions; @@ -43,18 +45,18 @@ public class RecentsAnimationWrapper { private boolean mWindowThresholdCrossed = false; - private final InputConsumerController mInputConsumer; - private final Supplier mTouchProxySupplier; + private final InputConsumerController mInputConsumerController; + private final Supplier mInputProxySupplier; - private TouchConsumer mTouchConsumer; + private InputConsumer mInputConsumer; private boolean mTouchInProgress; private boolean mFinishPending; - public RecentsAnimationWrapper(InputConsumerController inputConsumer, - Supplier touchProxySupplier) { - mInputConsumer = inputConsumer; - mTouchProxySupplier = touchProxySupplier; + public RecentsAnimationWrapper(InputConsumerController inputConsumerController, + Supplier inputProxySupplier) { + mInputConsumerController = inputConsumerController; + mInputProxySupplier = inputProxySupplier; } @UiThread @@ -132,15 +134,30 @@ public class RecentsAnimationWrapper { } } - public void enableTouchProxy() { - mInputConsumer.setTouchListener(this::onInputConsumerTouch); + public void enableInputProxy() { + mInputConsumerController.setInputListener(this::onInputConsumerEvent); } - private boolean onInputConsumerTouch(MotionEvent ev) { + private boolean onInputConsumerEvent(InputEvent ev) { + if (ev instanceof MotionEvent) { + onInputConsumerMotionEvent((MotionEvent) ev); + } else if (ev instanceof KeyEvent) { + if (mInputConsumer == null) { + mInputConsumer = mInputProxySupplier.get(); + } + mInputConsumer.onKeyEvent((KeyEvent) ev); + return true; + } + return false; + } + + private boolean onInputConsumerMotionEvent(MotionEvent ev) { int action = ev.getAction(); if (action == ACTION_DOWN) { mTouchInProgress = true; - mTouchConsumer = mTouchProxySupplier.get(); + if (mInputConsumer == null) { + mInputConsumer = mInputProxySupplier.get(); + } } else if (action == ACTION_CANCEL || action == ACTION_UP) { // Finish any pending actions mTouchInProgress = false; @@ -149,8 +166,8 @@ public class RecentsAnimationWrapper { finishAndClear(true /* toRecents */, null); } } - if (mTouchConsumer != null) { - mTouchConsumer.accept(ev); + if (mInputConsumer != null) { + mInputConsumer.onMotionEvent(ev); } return true; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionLog.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionLog.java index b542701922..4b660d4cf8 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionLog.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionLog.java @@ -42,7 +42,7 @@ public class TouchInteractionLog { getCurrentLog().add("[" + mDateFormat.format(mCalendar.getTime()) + "]"); } - public void setTouchConsumer(TouchConsumer consumer) { + public void setInputConsumer(InputConsumer consumer) { getCurrentLog().add("tc=" + consumer.getClass().getSimpleName()); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index 0ccd141a30..ddf3ad584a 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -159,7 +159,7 @@ public class TouchInteractionService extends Service { private InputConsumerController mInputConsumer; private SwipeSharedState mSwipeSharedState; - private TouchConsumer mConsumer = TouchConsumer.NO_OP; + private InputConsumer mConsumer = InputConsumer.NO_OP; private Choreographer mMainChoreographer; private InputEventReceiver mInputEventReceiver; @@ -226,34 +226,34 @@ public class TouchInteractionService extends Service { boolean useSharedState = mConsumer.isActive(); mConsumer.onConsumerAboutToBeSwitched(); mConsumer = newConsumer(useSharedState, event); - TOUCH_INTERACTION_LOG.setTouchConsumer(mConsumer); + TOUCH_INTERACTION_LOG.setInputConsumer(mConsumer); } TOUCH_INTERACTION_LOG.addMotionEvent(event); - mConsumer.accept(event); + mConsumer.onMotionEvent(event); } - private TouchConsumer newConsumer(boolean useSharedState, MotionEvent event) { + private InputConsumer newConsumer(boolean useSharedState, MotionEvent event) { RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0); if (!useSharedState) { mSwipeSharedState.clearAllState(); } if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher) { - return TouchConsumer.NO_OP; + return InputConsumer.NO_OP; } else if (mSwipeSharedState.goingToLauncher || mOverviewComponentObserver.getActivityControlHelper().isResumed()) { - return OverviewTouchConsumer.newInstance( + return OverviewInputConsumer.newInstance( mOverviewComponentObserver.getActivityControlHelper(), false); } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mOverviewComponentObserver.getActivityControlHelper().isInLiveTileMode()) { - return OverviewTouchConsumer.newInstance( + return OverviewInputConsumer.newInstance( mOverviewComponentObserver.getActivityControlHelper(), false); } else { ActivityControlHelper activityControl = mOverviewComponentObserver.getActivityControlHelper(); boolean shouldDefer = activityControl.deferStartingActivity(mActiveNavBarRegion, event); - return new OtherActivityTouchConsumer(this, runningTaskInfo, mRecentsModel, + return new OtherActivityInputConsumer(this, runningTaskInfo, mRecentsModel, mOverviewComponentObserver.getOverviewIntent(), activityControl, shouldDefer, mOverviewCallbacks, mTaskOverlayFactory, mInputConsumer, this::onConsumerInactive, mSwipeSharedState); @@ -263,9 +263,9 @@ public class TouchInteractionService extends Service { /** * To be called by the consumer when it's no longer active. */ - private void onConsumerInactive(TouchConsumer caller) { + private void onConsumerInactive(InputConsumer caller) { if (mConsumer == caller) { - mConsumer = TouchConsumer.NO_OP; + mConsumer = InputConsumer.NO_OP; } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java index f578149a98..4d0136e0d5 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -56,7 +56,6 @@ import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.SystemClock; -import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.View; @@ -274,7 +273,7 @@ public class WindowTransformSwipeHandler .createActivityInitListener(this::onActivityInit); mContinuingLastGesture = continuingLastGesture; mRecentsAnimationWrapper = new RecentsAnimationWrapper(inputConsumer, - this::createNewTouchProxyHandler); + this::createNewInputProxyHandler); mClipAnimationHelper = new ClipAnimationHelper(context); mTransformParams = new ClipAnimationHelper.TransformParams(); @@ -719,7 +718,7 @@ public class WindowTransformSwipeHandler } @UiThread - private TouchConsumer createNewTouchProxyHandler() { + private InputConsumer createNewInputProxyHandler() { mCurrentShift.finishAnimation(); if (mLauncherTransitionController != null) { mLauncherTransitionController.getAnimationPlayer().end(); @@ -729,7 +728,7 @@ public class WindowTransformSwipeHandler setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha); } - return OverviewTouchConsumer.newInstance(mActivityControlHelper, true); + return OverviewInputConsumer.newInstance(mActivityControlHelper, true); } @UiThread @@ -823,7 +822,7 @@ public class WindowTransformSwipeHandler setShelfState(ShelfAnimState.CANCEL, LINEAR, 0); duration = Math.max(MIN_OVERSHOOT_DURATION, duration); } else if (endTarget == RECENTS) { - mRecentsAnimationWrapper.enableTouchProxy(); + mRecentsAnimationWrapper.enableInputProxy(); if (mRecentsView != null) { duration = Math.max(duration, mRecentsView.getScroller().getDuration()); } diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java index c24396862c..fe789aadef 100644 --- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java +++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java @@ -63,11 +63,11 @@ public class StartLauncherViaGestureTests extends AbstractQuickStepTest { public void testPressHome() { runTest(enterEvt(Launcher.ON_CREATE_EVT), exitEvt(Launcher.ON_CREATE_EVT), - enterEvt(OtherActivityTouchConsumer.DOWN_EVT), - exitEvt(OtherActivityTouchConsumer.DOWN_EVT)); + enterEvt(OtherActivityInputConsumer.DOWN_EVT), + exitEvt(OtherActivityInputConsumer.DOWN_EVT)); - runTest(enterEvt(OtherActivityTouchConsumer.DOWN_EVT), - exitEvt(OtherActivityTouchConsumer.DOWN_EVT), + runTest(enterEvt(OtherActivityInputConsumer.DOWN_EVT), + exitEvt(OtherActivityInputConsumer.DOWN_EVT), enterEvt(Launcher.ON_CREATE_EVT), exitEvt(Launcher.ON_CREATE_EVT)); } From ccbb4f3bf3a84172a1c8da9dc8ef4e00d6e92042 Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 27 Feb 2019 12:16:37 -0800 Subject: [PATCH 09/44] Fix RecentTasksList change id incrementing The change id in RecentTasksList would never actually increment as we never register it as a TaskStackListener. As a result, we always execute the result callback immediately when getting a task and the task list is always valid. In addition, when the list IS up to date, we still fetch the list again in the background when we should instead just return early. This CL addresses both of these issues. Test: Added logs to see that task loading wasn't happening when lists are the same Change-Id: I228f8d7cd3cb22ef88d71e1bee40708c2bcf26d6 --- quickstep/src/com/android/quickstep/RecentTasksList.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java index 00b3e90938..e15a3f1ca1 100644 --- a/quickstep/src/com/android/quickstep/RecentTasksList.java +++ b/quickstep/src/com/android/quickstep/RecentTasksList.java @@ -60,6 +60,7 @@ public class RecentTasksList extends TaskStackChangeListener { mBgThreadExecutor = BackgroundExecutor.get(); mKeyguardManager = new KeyguardManagerCompat(context); mChangeId = 1; + ActivityManagerWrapper.getInstance().registerTaskStackListener(this); } /** @@ -97,6 +98,7 @@ public class RecentTasksList extends TaskStackChangeListener { if (mLastLoadedId == mChangeId && (!mLastLoadHadKeysOnly || loadKeysOnly)) { // The list is up to date, callback with the same list mMainThreadExecutor.execute(resultCallback); + return requestLoadId; } // Kick off task loading in the background From 1c2b6c45949723243b12a85511cebc775962d068 Mon Sep 17 00:00:00 2001 From: Kevin Date: Tue, 26 Feb 2019 13:24:54 -0800 Subject: [PATCH 10/44] Add recycler view for recents Go Add recycler view to view hierarchy for recents Go to hold the list of recent tasks. Bug: 114136250 Test: Manual test, see view in place Change-Id: I255bb4a7737726b0e211b52aec3f2fb8f4723513 --- .../res/layout/icon_recents_root_view.xml | 10 ++++------ .../android/quickstep/views/IconRecentsView.java | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/go/quickstep/res/layout/icon_recents_root_view.xml b/go/quickstep/res/layout/icon_recents_root_view.xml index 82d58909e5..6c5095000b 100644 --- a/go/quickstep/res/layout/icon_recents_root_view.xml +++ b/go/quickstep/res/layout/icon_recents_root_view.xml @@ -18,13 +18,11 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:gravity="center"> - - + + android:scrollbars="none"/> \ No newline at end of file diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java index 00415fe1a0..15da10cdd9 100644 --- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java +++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java @@ -15,12 +15,19 @@ */ package com.android.quickstep.views; +import static androidx.recyclerview.widget.LinearLayoutManager.VERTICAL; + import android.content.Context; import android.util.AttributeSet; import android.util.FloatProperty; import android.view.ViewDebug; import android.widget.FrameLayout; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.launcher3.R; +import com.android.quickstep.RecentsModel; import com.android.quickstep.TaskAdapter; import com.android.systemui.shared.recents.model.Task; @@ -72,19 +79,25 @@ public final class IconRecentsView extends FrameLayout { // TODO: Write a recents task list observer that creates/updates tasks and signals task adapter. private static final ArrayList DUMMY_TASK_LIST = new ArrayList<>(); + private final Context mContext; private float mTranslationYFactor; private TaskAdapter mTaskAdapter; + private RecyclerView mTaskRecyclerView; public IconRecentsView(Context context, AttributeSet attrs) { super(context, attrs); + mContext = context; } @Override protected void onFinishInflate() { super.onFinishInflate(); mTaskAdapter = new TaskAdapter(DUMMY_TASK_LIST); - // TODO: Hook task adapter up to recycler view. + mTaskRecyclerView = findViewById(R.id.recent_task_recycler_view); + mTaskRecyclerView.setAdapter(mTaskAdapter); + mTaskRecyclerView.setLayoutManager( + new LinearLayoutManager(mContext, VERTICAL, true /* reverseLayout */)); } public void setTranslationYFactor(float translationFactor) { From ac60d91cece8c44de967cb3f844b76e25a72d40e Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Wed, 27 Feb 2019 16:40:59 -0800 Subject: [PATCH 11/44] Fix some issues with MotionPauseDetector - Orthogonal displacement now checks angle instead of fixed amount - Only allow timeout when passed min primary displacement and not orthogonal displacement - Don't set timeout for !SWIPE_HOME, which should never detect pause Change-Id: I3d810831316affff138968dfc62b921b20c752c5 --- quickstep/res/values/dimens.xml | 1 - .../quickstep/util/MotionPauseDetector.java | 14 ++++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 2626481c3d..04fd59ce07 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -39,7 +39,6 @@ 0.285dp 0.5dp 48dp - 48dp 50dp diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java index 21d814454b..8a117c8592 100644 --- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java +++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java @@ -40,7 +40,6 @@ public class MotionPauseDetector { private final float mSpeedSomewhatFast; private final float mSpeedFast; private final float mMinDisplacementForPause; - private final float mMaxOrthogonalDisplacementForPause; private final Alarm mForcePauseTimeout; private Long mPreviousTime = null; @@ -62,8 +61,6 @@ public class MotionPauseDetector { mSpeedSomewhatFast = res.getDimension(R.dimen.motion_pause_detector_speed_somewhat_fast); mSpeedFast = res.getDimension(R.dimen.motion_pause_detector_speed_fast); mMinDisplacementForPause = res.getDimension(R.dimen.motion_pause_detector_min_displacement); - mMaxOrthogonalDisplacementForPause = res.getDimension( - R.dimen.motion_pause_detector_max_orthogonal_displacement); mForcePauseTimeout = new Alarm(); mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */)); } @@ -77,7 +74,6 @@ public class MotionPauseDetector { if (mOnMotionPauseListener != null) { mOnMotionPauseListener.onMotionPauseChanged(mIsPaused); } - mForcePauseTimeout.setAlarm(FORCE_PAUSE_TIMEOUT); } /** @@ -94,6 +90,7 @@ public class MotionPauseDetector { if (mFirstOrthogonalPosition == null) { mFirstOrthogonalPosition = orthogonalPosition; } + mForcePauseTimeout.setAlarm(FORCE_PAUSE_TIMEOUT); long time = SystemClock.uptimeMillis(); if (mPreviousTime != null && mPreviousPosition != null) { long changeInTime = Math.max(1, time - mPreviousTime); @@ -108,7 +105,6 @@ public class MotionPauseDetector { } mPreviousTime = time; mPreviousPosition = position; - mForcePauseTimeout.setAlarm(FORCE_PAUSE_TIMEOUT); } private void checkMotionPaused(float velocity, float prevVelocity, @@ -135,9 +131,11 @@ public class MotionPauseDetector { } } boolean passedMinDisplacement = totalDisplacement.primary >= mMinDisplacementForPause; - boolean passedMaxOrthogonalDisplacement = - totalDisplacement.orthogonal >= mMaxOrthogonalDisplacementForPause; - isPaused &= passedMinDisplacement && !passedMaxOrthogonalDisplacement; + boolean isDisplacementOrthogonal = totalDisplacement.orthogonal > totalDisplacement.primary; + if (!passedMinDisplacement || isDisplacementOrthogonal) { + mForcePauseTimeout.cancelAlarm(); + isPaused = false; + } updatePaused(isPaused); } From 72dae7f4ce5e44ffed99b14b9f9a290df0d21b9e Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Wed, 27 Feb 2019 16:58:57 -0800 Subject: [PATCH 12/44] Inherit TextAppearance.DeviceDefault for the RRO text font Bug: 126229665 Change-Id: I54f59f64888847c8a3ce2ea63be12a4c9199bb05 --- res/values/styles.xml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/res/values/styles.xml b/res/values/styles.xml index f0955b3a52..252cae1461 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -126,7 +126,7 @@ - - +