Merge "TAPL: increasing FLINGS_FOR_DISMISS_LIMIT" into ub-launcher3-master
diff --git a/Android.mk b/Android.mk
index 2feb141..25593e6 100644
--- a/Android.mk
+++ b/Android.mk
@@ -24,7 +24,10 @@
LOCAL_AAPT2_ONLY := true
LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_JAVA_LIBRARIES := libPluginCore
+ifneq (,$(wildcard frameworks/base))
+else
+ LOCAL_STATIC_JAVA_LIBRARIES:= libPluginCore
+endif
LOCAL_SRC_FILES := \
$(call all-java-files-under, src_plugins)
@@ -197,6 +200,7 @@
LOCAL_PRIVILEGED_MODULE := true
LOCAL_PRODUCT_MODULE := true
LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3
+LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
LOCAL_RESOURCE_DIR := \
$(LOCAL_PATH)/quickstep/res \
diff --git a/go/quickstep/res/drawable/clear_all_button.xml b/go/quickstep/res/drawable/clear_all_button.xml
new file mode 100644
index 0000000..acac32d
--- /dev/null
+++ b/go/quickstep/res/drawable/clear_all_button.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@color/clear_all_button_bg"/>
+ <corners android:radius="4dp"/>
+</shape>
diff --git a/go/quickstep/res/layout/icon_recents_root_view.xml b/go/quickstep/res/layout/icon_recents_root_view.xml
index ff3dcef9..fddb1d3 100644
--- a/go/quickstep/res/layout/icon_recents_root_view.xml
+++ b/go/quickstep/res/layout/icon_recents_root_view.xml
@@ -19,13 +19,31 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
- <androidx.recyclerview.widget.RecyclerView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/recent_task_recycler_view"
+ <LinearLayout
+ android:id="@+id/recent_task_content_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:scrollbars="none"
- android:visibility="gone"/>
+ 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:layout_weight="1"
+ android:scrollbars="none"/>
+ <Button
+ android:id="@+id/clear_all_button"
+ android:layout_width="@dimen/clear_all_button_width"
+ android:layout_height="@dimen/clear_all_button_height"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginVertical="@dimen/task_item_half_vert_margin"
+ 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"
diff --git a/go/quickstep/res/values/colors.xml b/go/quickstep/res/values/colors.xml
new file mode 100644
index 0000000..ff9dc9c
--- /dev/null
+++ b/go/quickstep/res/values/colors.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<resources>
+ <color name="clear_all_button_bg">#FFDADCE0</color>
+ <color name="clear_all_button_text">#FF5F6368</color>
+</resources>
diff --git a/go/quickstep/res/values/dimens.xml b/go/quickstep/res/values/dimens.xml
index 7269704..e2fa387 100644
--- a/go/quickstep/res/values/dimens.xml
+++ b/go/quickstep/res/values/dimens.xml
@@ -20,4 +20,7 @@
<dimen name="task_thumbnail_height">60dp</dimen>
<dimen name="task_thumbnail_width">36dp</dimen>
<dimen name="task_icon_size">36dp</dimen>
+
+ <dimen name="clear_all_button_width">106dp</dimen>
+ <dimen name="clear_all_button_height">36dp</dimen>
</resources>
\ No newline at end of file
diff --git a/go/quickstep/src/com/android/quickstep/TaskInputController.java b/go/quickstep/src/com/android/quickstep/TaskActionController.java
similarity index 64%
rename from go/quickstep/src/com/android/quickstep/TaskInputController.java
rename to go/quickstep/src/com/android/quickstep/TaskActionController.java
index 8433007..b2d495b 100644
--- a/go/quickstep/src/com/android/quickstep/TaskInputController.java
+++ b/go/quickstep/src/com/android/quickstep/TaskActionController.java
@@ -23,24 +23,25 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
/**
- * Controller responsible for task logic that occurs on various input to the recents view.
+ * Controller that provides logic for task-related commands on recents and updating the model/view
+ * as appropriate.
*/
-public final class TaskInputController {
+public final class TaskActionController {
private final TaskListLoader mLoader;
private final TaskAdapter mAdapter;
- public TaskInputController(TaskListLoader loader,TaskAdapter adapter) {
+ public TaskActionController(TaskListLoader loader, TaskAdapter adapter) {
mLoader = loader;
mAdapter = adapter;
}
/**
- * Logic that occurs when a task view is tapped. Launches the respective task.
+ * Launch the task associated with the task holder, animating into the app.
*
- * @param viewHolder the task view holder that has been tapped
+ * @param viewHolder the task view holder to launch
*/
- public void onTaskClicked(TaskHolder viewHolder) {
+ public void launchTask(TaskHolder viewHolder) {
TaskItemView itemView = (TaskItemView) (viewHolder.itemView);
View v = itemView.getThumbnailView();
int left = 0;
@@ -53,7 +54,12 @@
opts, null /* resultCallback */, null /* resultCallbackHandler */);
}
- public void onTaskSwiped(TaskHolder viewHolder) {
+ /**
+ * Removes the task holder and the task, updating the model and the view.
+ *
+ * @param viewHolder the task view holder to remove
+ */
+ public void removeTask(TaskHolder viewHolder) {
int position = viewHolder.getAdapterPosition();
Task task = viewHolder.getTask();
ActivityManagerWrapper.getInstance().removeTask(task.key.id);
@@ -61,6 +67,14 @@
mAdapter.notifyItemRemoved(position);
}
- // TODO: Implement "Clear all" and notify adapter that data has updated
-
+ /**
+ * Clears all tasks and updates the model and view.
+ */
+ public void clearAllTasks() {
+ // TODO: Play an animation so transition is more natural.
+ int count = mAdapter.getItemCount();
+ ActivityManagerWrapper.getInstance().removeAllRecentTasks();
+ mLoader.clearAllTasks();
+ mAdapter.notifyItemRangeRemoved(0 /* positionStart */, count);
+ }
}
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
index 9a2c0f8..e56cc51 100644
--- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
@@ -39,14 +39,14 @@
private static final String TAG = "TaskAdapter";
private final TaskListLoader mLoader;
private final ArrayMap<Integer, TaskItemView> mTaskIdToViewMap = new ArrayMap<>();
- private TaskInputController mInputController;
+ private TaskActionController mTaskActionController;
public TaskAdapter(@NonNull TaskListLoader loader) {
mLoader = loader;
}
- public void setInputController(TaskInputController inputController) {
- mInputController = inputController;
+ public void setActionController(TaskActionController taskActionController) {
+ mTaskActionController = taskActionController;
}
/**
@@ -64,7 +64,7 @@
TaskItemView itemView = (TaskItemView) LayoutInflater.from(parent.getContext())
.inflate(R.layout.task_item_view, parent, false);
TaskHolder holder = new TaskHolder(itemView);
- itemView.setOnClickListener(view -> mInputController.onTaskClicked(holder));
+ itemView.setOnClickListener(view -> mTaskActionController.launchTask(holder));
return holder;
}
diff --git a/go/quickstep/src/com/android/quickstep/TaskListLoader.java b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
index e6d1a22..c86c24e 100644
--- a/go/quickstep/src/com/android/quickstep/TaskListLoader.java
+++ b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
@@ -93,6 +93,13 @@
}
/**
+ * Clears the current task list.
+ */
+ void clearAllTasks() {
+ mTaskList.clear();
+ }
+
+ /**
* Loads task content for a list of tasks, including the label, icon, and thumbnail. For content
* that isn't cached, load the content asynchronously in the background.
*
diff --git a/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java b/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java
index 2a53917..98407d8 100644
--- a/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java
+++ b/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java
@@ -26,11 +26,11 @@
*/
public final class TaskSwipeCallback extends ItemTouchHelper.SimpleCallback {
- private final TaskInputController mTaskInputController;
+ private final TaskActionController mTaskActionController;
- public TaskSwipeCallback(TaskInputController inputController) {
+ public TaskSwipeCallback(TaskActionController taskActionController) {
super(0 /* dragDirs */, RIGHT);
- mTaskInputController = inputController;
+ mTaskActionController = taskActionController;
}
@Override
@@ -42,7 +42,7 @@
@Override
public void onSwiped(ViewHolder viewHolder, int direction) {
if (direction == RIGHT) {
- mTaskInputController.onTaskSwiped((TaskHolder) viewHolder);
+ mTaskActionController.removeTask((TaskHolder) viewHolder);
}
}
}
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index 504f640..0e5dbc4 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -36,7 +36,7 @@
import com.android.launcher3.R;
import com.android.quickstep.RecentsToActivityHelper;
import com.android.quickstep.TaskAdapter;
-import com.android.quickstep.TaskInputController;
+import com.android.quickstep.TaskActionController;
import com.android.quickstep.TaskListLoader;
import com.android.quickstep.TaskSwipeCallback;
@@ -74,19 +74,20 @@
private final Context mContext;
private final TaskListLoader mTaskLoader;
private final TaskAdapter mTaskAdapter;
- private final TaskInputController mTaskInputController;
+ private final TaskActionController mTaskActionController;
private RecentsToActivityHelper mActivityHelper;
private RecyclerView mTaskRecyclerView;
private View mEmptyView;
+ private View mContentView;
public IconRecentsView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
mTaskLoader = new TaskListLoader(mContext);
mTaskAdapter = new TaskAdapter(mTaskLoader);
- mTaskInputController = new TaskInputController(mTaskLoader, mTaskAdapter);
- mTaskAdapter.setInputController(mTaskInputController);
+ mTaskActionController = new TaskActionController(mTaskLoader, mTaskAdapter);
+ mTaskAdapter.setActionController(mTaskActionController);
}
@Override
@@ -98,10 +99,11 @@
mTaskRecyclerView.setLayoutManager(
new LinearLayoutManager(mContext, VERTICAL, true /* reverseLayout */));
ItemTouchHelper helper = new ItemTouchHelper(
- new TaskSwipeCallback(mTaskInputController));
+ new TaskSwipeCallback(mTaskActionController));
helper.attachToRecyclerView(mTaskRecyclerView);
mEmptyView = findViewById(R.id.recent_task_empty_view);
+ mContentView = findViewById(R.id.recent_task_content_view);
mTaskAdapter.registerAdapterDataObserver(new AdapterDataObserver() {
@Override
public void onChanged() {
@@ -113,6 +115,9 @@
updateContentViewVisibility();
}
});
+
+ View clearAllView = findViewById(R.id.clear_all_button);
+ clearAllView.setOnClickListener(v -> mTaskActionController.clearAllTasks());
}
}
@@ -162,11 +167,11 @@
private void updateContentViewVisibility() {
int taskListSize = mTaskLoader.getCurrentTaskList().size();
if (mEmptyView.getVisibility() != VISIBLE && taskListSize == 0) {
- crossfadeViews(mEmptyView, mTaskRecyclerView);
+ crossfadeViews(mEmptyView, mContentView);
mActivityHelper.leaveRecents();
}
- if (mTaskRecyclerView.getVisibility() != VISIBLE && taskListSize > 0) {
- crossfadeViews(mTaskRecyclerView, mEmptyView);
+ if (mContentView.getVisibility() != VISIBLE && taskListSize > 0) {
+ crossfadeViews(mContentView, mEmptyView);
}
}
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 f0bc223..0924f38 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java
@@ -173,6 +173,12 @@
return true;
}
+ public void setCancelWithDeferredScreenshot(boolean deferredWithScreenshot) {
+ if (targetSet != null) {
+ targetSet.controller.setCancelWithDeferredScreenshot(deferredWithScreenshot);
+ }
+ }
+
public SwipeAnimationTargetSet getController() {
return targetSet;
}
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 eb1e7b4..e8d4c19 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -1136,6 +1136,7 @@
mLauncherTransitionController = null;
}
mActivityControlHelper.onSwipeUpComplete(mActivity);
+ mRecentsAnimationWrapper.setCancelWithDeferredScreenshot(true);
// Animate the first icon.
mRecentsView.animateUpRunningTaskIconScale(mLiveTileOverlay.cancelIconAnimation());
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java
index 62f2183..94e704a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java
@@ -38,9 +38,16 @@
*/
public class RecentsAnimationListenerSet implements RecentsAnimationListener {
+ // The actual app surface is replaced by a screenshot upon recents animation cancelation when
+ // deferredWithScreenshot is true. Launcher takes the responsibility to clean up this screenshot
+ // after app transition is finished. This delay is introduced to cover the app transition
+ // period of time.
+ private final int TRANSITION_DELAY = 100;
+
private final Set<SwipeAnimationListener> mListeners = new ArraySet<>();
private final boolean mShouldMinimizeSplitScreen;
private final Consumer<SwipeAnimationTargetSet> mOnFinishListener;
+ private RecentsAnimationControllerCompat mController;
public RecentsAnimationListenerSet(boolean shouldMinimizeSplitScreen,
Consumer<SwipeAnimationTargetSet> onFinishListener) {
@@ -64,6 +71,7 @@
public final void onAnimationStart(RecentsAnimationControllerCompat controller,
RemoteAnimationTargetCompat[] targets, Rect homeContentInsets,
Rect minimizedHomeBounds) {
+ mController = controller;
SwipeAnimationTargetSet targetSet = new SwipeAnimationTargetSet(controller, targets,
homeContentInsets, minimizedHomeBounds, mShouldMinimizeSplitScreen,
mOnFinishListener);
@@ -75,12 +83,17 @@
}
@Override
- public final void onAnimationCanceled() {
+ public final void onAnimationCanceled(boolean deferredWithScreenshot) {
Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(), () -> {
for (SwipeAnimationListener listener : getListeners()) {
listener.onRecentsAnimationCanceled();
}
});
+ // TODO: handle the transition better instead of simply using a transition delay.
+ if (deferredWithScreenshot) {
+ MAIN_THREAD_EXECUTOR.getHandler().postDelayed(() -> mController.cleanupScreenshot(),
+ TRANSITION_DELAY);
+ }
}
private SwipeAnimationListener[] getListeners() {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index 8f2a2d7..8da1d2b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -74,12 +74,7 @@
@Override
public void startHome() {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- takeScreenshotAndFinishRecentsAnimation(true,
- () -> mActivity.getStateManager().goToState(NORMAL));
- } else {
- mActivity.getStateManager().goToState(NORMAL);
- }
+ mActivity.getStateManager().goToState(NORMAL);
}
@Override
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 37febf9a..a02df62 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
@@ -96,7 +96,6 @@
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.util.SwipeAnimationTargetSet;
import com.android.quickstep.util.TaskViewDrawable;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -106,7 +105,6 @@
import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.WindowCallbacksCompat;
import java.util.ArrayList;
import java.util.function.Consumer;
@@ -1608,50 +1606,4 @@
mRecentsAnimationWrapper.finish(toRecents, onFinishComplete);
}
-
- public void takeScreenshotAndFinishRecentsAnimation(boolean toRecents,
- Runnable onFinishComplete) {
- if (mRecentsAnimationWrapper == null || getRunningTaskView() == null) {
- if (onFinishComplete != null) {
- onFinishComplete.run();
- }
- return;
- }
-
- SwipeAnimationTargetSet controller = mRecentsAnimationWrapper.getController();
- if (controller != null) {
- // Update the screenshot of the task
- ThumbnailData taskSnapshot = controller.screenshotTask(mRunningTaskId);
- TaskView taskView = updateThumbnail(mRunningTaskId, taskSnapshot);
- if (taskView != null) {
- taskView.setShowScreenshot(true);
- // Defer finishing the animation until the next launcher frame with the
- // new thumbnail
- new WindowCallbacksCompat(taskView) {
-
- // The number of frames to defer until we actually finish the animation
- private int mDeferFrameCount = 2;
-
- @Override
- public void onPostDraw(Canvas canvas) {
- if (mDeferFrameCount > 0) {
- mDeferFrameCount--;
- // Workaround, detach and reattach to invalidate the root node for
- // another draw
- detach();
- attach();
- taskView.invalidate();
- return;
- }
-
- detach();
- mRecentsAnimationWrapper.finish(toRecents, () -> {
- onFinishComplete.run();
- mRunningTaskId = -1;
- });
- }
- }.attach();
- }
- }
- }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
index 682152e..d15a392 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
@@ -208,13 +208,7 @@
R.layout.task_view_menu_option, this, false);
menuOption.setIconAndLabelFor(
menuOptionView.findViewById(R.id.icon), menuOptionView.findViewById(R.id.text));
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- menuOptionView.setOnClickListener(
- view -> mTaskView.getRecentsView().takeScreenshotAndFinishRecentsAnimation(true,
- () -> onClickListener.onClick(view)));
- } else {
- menuOptionView.setOnClickListener(onClickListener);
- }
+ menuOptionView.setOnClickListener(onClickListener);
mOptionLayout.addView(menuOptionView);
}
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 98495db..38aaac5 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
@@ -261,11 +261,10 @@
Handler resultCallbackHandler) {
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
if (isRunningTask()) {
- getRecentsView().finishRecentsAnimation(false,
+ getRecentsView().finishRecentsAnimation(false /* toRecents */,
() -> resultCallbackHandler.post(() -> resultCallback.accept(true)));
} else {
- getRecentsView().takeScreenshotAndFinishRecentsAnimation(true,
- () -> launchTaskInternal(animate, resultCallback, resultCallbackHandler));
+ launchTaskInternal(animate, resultCallback, resultCallbackHandler);
}
} else {
launchTaskInternal(animate, resultCallback, resultCallbackHandler);
diff --git a/tests/Android.mk b/tests/Android.mk
index a787537..bf5cb29 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -23,8 +23,12 @@
androidx.annotation_annotation \
androidx.test.runner \
androidx.test.rules \
- androidx.test.uiautomator_uiautomator \
- libSharedSystemUI
+ androidx.test.uiautomator_uiautomator
+
+ifneq (,$(wildcard frameworks/base))
+else
+ LOCAL_STATIC_JAVA_LIBRARIES += libSharedSystemUI
+endif
LOCAL_SRC_FILES := $(call all-java-files-under, tapl) \
../quickstep/src/com/android/quickstep/SwipeUpSetting.java \
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index d39a38e..fbeb3a2 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -17,6 +17,7 @@
package com.android.launcher3.tapl;
import android.graphics.Point;
+import android.os.SystemClock;
import android.view.MotionEvent;
import android.widget.TextView;
@@ -41,10 +42,12 @@
*/
public AppIconMenu openMenu() {
final Point iconCenter = mObject.getVisibleCenter();
- mLauncher.sendPointer(MotionEvent.ACTION_DOWN, iconCenter);
+ final long downTime = SystemClock.uptimeMillis();
+ mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, iconCenter);
final UiObject2 deepShortcutsContainer = mLauncher.waitForLauncherObject(
"deep_shortcuts_container");
- mLauncher.sendPointer(MotionEvent.ACTION_UP, iconCenter);
+ mLauncher.sendPointer(
+ downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, iconCenter);
return new AppIconMenu(mLauncher, deepShortcutsContainer);
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 2cde8ec..ef509ed 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -22,6 +22,10 @@
import static org.junit.Assert.assertTrue;
+import android.graphics.Point;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+
import androidx.annotation.NonNull;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.Until;
@@ -33,6 +37,8 @@
* indicate Launcher as long as Launcher is not in Overview state.
*/
public class Background extends LauncherInstrumentation.VisibleContainer {
+ private static final int ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION = 500;
+ private static final int ZERO_BUTTON_SWIPE_UP_HOLD_DURATION = 400;
Background(LauncherInstrumentation launcher) {
super(launcher);
@@ -59,19 +65,41 @@
}
protected void goToOverviewUnchecked(int expectedState) {
- if (mLauncher.isSwipeUpEnabled()) {
- final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
- final int startY = getSwipeStartY();
- final int swipeHeight = mLauncher.getTestInfo(
- getSwipeHeightRequestName()).
- getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ switch (mLauncher.getNavigationModel()) {
+ case ZERO_BUTTON: {
+ final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
+ final int startY = getSwipeStartY();
+ final int swipeHeight = mLauncher.getTestInfo(getSwipeHeightRequestName()).
+ getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ final Point start = new Point(centerX, startY);
+ final Point end =
+ new Point(centerX, startY - swipeHeight - mLauncher.getTouchSlop());
- mLauncher.swipe(
- centerX, startY, centerX,
- startY - swipeHeight - mLauncher.getTouchSlop(),
- expectedState);
- } else {
- mLauncher.waitForSystemUiObject("recent_apps").click();
+ final long downTime = SystemClock.uptimeMillis();
+ mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start);
+ mLauncher.movePointer(downTime, ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION, start, end);
+ LauncherInstrumentation.sleep(ZERO_BUTTON_SWIPE_UP_HOLD_DURATION);
+ mLauncher.sendPointer(
+ downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, end);
+ break;
+ }
+
+ case TWO_BUTTON: {
+ final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
+ final int startY = getSwipeStartY();
+ final int swipeHeight = mLauncher.getTestInfo(getSwipeHeightRequestName()).
+ getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+
+ mLauncher.swipe(
+ centerX, startY, centerX,
+ startY - swipeHeight - mLauncher.getTouchSlop(),
+ expectedState);
+ break;
+ }
+
+ case THREE_BUTTON:
+ mLauncher.waitForSystemUiObject("recent_apps").click();
+ break;
}
}
@@ -80,6 +108,6 @@
}
protected int getSwipeStartY() {
- return mLauncher.waitForSystemUiObject("home").getVisibleBounds().centerY();
+ return mLauncher.waitForSystemUiObject("navigation_bar_frame").getVisibleBounds().centerY();
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 642735e..234c3bf 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -44,8 +44,10 @@
* Flings forward (left) and waits the fling's end.
*/
public void flingForward() {
- final UiObject2 overview = verifyActiveContainer();
LauncherInstrumentation.log("Overview.flingForward before fling");
+ final UiObject2 overview = verifyActiveContainer();
+ final int margin = (int) (50 * mLauncher.getDisplayDensity()) + 1;
+ overview.setGestureMargins(margin, 0, 0, 0);
overview.fling(Direction.LEFT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
mLauncher.waitForIdle();
verifyActiveContainer();
@@ -71,8 +73,10 @@
* Flings backward (right) and waits the fling's end.
*/
public void flingBackward() {
- final UiObject2 overview = verifyActiveContainer();
LauncherInstrumentation.log("Overview.flingBackward before fling");
+ final UiObject2 overview = verifyActiveContainer();
+ final int margin = (int) (50 * mLauncher.getDisplayDensity()) + 1;
+ overview.setGestureMargins(0, 0, margin, 0);
overview.fling(Direction.RIGHT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
mLauncher.waitForIdle();
verifyActiveContainer();
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index e3850ff..f44022b 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -16,6 +16,7 @@
package com.android.launcher3.tapl;
+import static com.android.launcher3.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
import static com.android.systemui.shared.system.SettingsCompat.SWIPE_UP_SETTING_NAME;
import android.app.ActivityManager;
@@ -30,14 +31,17 @@
import android.os.SystemClock;
import android.provider.Settings;
import android.util.Log;
+import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.Configurator;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.Until;
@@ -59,6 +63,7 @@
public final class LauncherInstrumentation {
private static final String TAG = "Tapl";
+ private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 20;
// Types for launcher containers that the user is interacting with. "Background" is a
// pseudo-container corresponding to inactive launcher covered by another app.
@@ -66,6 +71,8 @@
WORKSPACE, ALL_APPS, OVERVIEW, WIDGETS, BACKGROUND, BASE_OVERVIEW
}
+ public enum NavigationModel {ZERO_BUTTON, TWO_BUTTON, THREE_BUTTON}
+
// Base class for launcher containers.
static abstract class VisibleContainer {
protected final LauncherInstrumentation mLauncher;
@@ -150,7 +157,11 @@
sActiveContainer = new WeakReference<>(container);
}
- public boolean isSwipeUpEnabled() {
+ public NavigationModel getNavigationModel() {
+ return isSwipeUpEnabled() ? NavigationModel.TWO_BUTTON : NavigationModel.THREE_BUTTON;
+ }
+
+ private boolean isSwipeUpEnabled() {
final boolean swipeUpEnabledDefaultValue = SwipeUpSetting.isSwipeUpEnabledDefaultValue();
return SwipeUpSetting.isSwipeUpSettingAvailable() ?
Settings.Secure.getInt(
@@ -286,24 +297,49 @@
// We need waiting for any accessibility event generated after pressing Home because
// otherwise waitForIdle may return immediately in case when there was a big enough pause in
// accessibility events prior to pressing Home.
- executeAndWaitForEvent(
- () -> {
- log("LauncherInstrumentation.pressHome before clicking");
- waitForSystemUiObject("home").click();
- },
- event -> true,
- "Pressing Home didn't produce any events");
- mDevice.waitForIdle();
+ if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
+ if (hasLauncherObject(WORKSPACE_RES_ID)) {
+ log("0-button pressHome: already in workspace");
+ } else if (hasLauncherObject(OVERVIEW_RES_ID)) {
+ log("0-button pressHome: overview");
+ mDevice.pressHome();
+ } else if (hasLauncherObject(WIDGETS_RES_ID)) {
+ log("0-button pressHome: widgets");
+ mDevice.pressHome();
+ } else if (hasLauncherObject(APPS_RES_ID)) {
+ log("0-button pressHome: all apps");
+ mDevice.pressHome();
+ } else {
+ log("0-button pressHome: another app");
+ assertTrue("Launcher is visible, don't know how to go home",
+ !mDevice.hasObject(By.pkg(getLauncherPackageName())));
+ final UiObject2 navBar = waitForSystemUiObject("navigation_bar_frame");
- // Temporarily press home twice as the first click sometimes gets ignored (b/124239413)
- executeAndWaitForEvent(
- () -> {
- log("LauncherInstrumentation.pressHome before clicking");
- waitForSystemUiObject("home").click();
- },
- event -> true,
- "Pressing Home didn't produce any events");
- mDevice.waitForIdle();
+ swipe(
+ navBar.getVisibleBounds().centerX(), navBar.getVisibleBounds().centerY(),
+ navBar.getVisibleBounds().centerX(), 0,
+ BACKGROUND_APP_STATE_ORDINAL, ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME);
+ }
+ } else {
+ executeAndWaitForEvent(
+ () -> {
+ log("LauncherInstrumentation.pressHome before clicking");
+ waitForSystemUiObject("home").click();
+ },
+ event -> true,
+ "Pressing Home didn't produce any events");
+ mDevice.waitForIdle();
+
+ // Temporarily press home twice as the first click sometimes gets ignored (b/124239413)
+ executeAndWaitForEvent(
+ () -> {
+ log("LauncherInstrumentation.pressHome before clicking");
+ waitForSystemUiObject("home").click();
+ },
+ event -> true,
+ "Pressing Home didn't produce any events");
+ mDevice.waitForIdle();
+ }
return getWorkspace();
}
@@ -424,6 +460,11 @@
return object;
}
+ @Nullable
+ private boolean hasLauncherObject(String resId) {
+ return mDevice.hasObject(getLauncherObjectSelector(resId));
+ }
+
@NonNull
UiObject2 waitForLauncherObject(String resName) {
final BySelector selector = getLauncherObjectSelector(resName);
@@ -446,8 +487,12 @@
}
void swipe(int startX, int startY, int endX, int endY, int expectedState) {
+ swipe(startX, startY, endX, endY, expectedState, 60);
+ }
+
+ void swipe(int startX, int startY, int endX, int endY, int expectedState, int steps) {
final Bundle parcel = (Bundle) executeAndWaitForEvent(
- () -> mDevice.swipe(startX, startY, endX, endY, 60),
+ () -> mDevice.swipe(startX, startY, endX, endY, steps),
event -> TestProtocol.SWITCHED_TO_STATE_MESSAGE.equals(event.getClassName()),
"Swipe failed to receive an event for the swipe end: " + startX + ", " + startY
+ ", " + endX + ", " + endY);
@@ -459,13 +504,6 @@
mDevice.waitForIdle();
}
- void sendPointer(int action, Point point) {
- final MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(),
- SystemClock.uptimeMillis(), action, point.x, point.y, 0);
- mInstrumentation.sendPointerSync(event);
- event.recycle();
- }
-
float getDisplayDensity() {
return mInstrumentation.getTargetContext().getResources().getDisplayMetrics().density;
}
@@ -473,4 +511,51 @@
int getTouchSlop() {
return ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
+
+ private static MotionEvent getMotionEvent(long downTime, long eventTime, int action,
+ float x, float y) {
+ MotionEvent.PointerProperties properties = new MotionEvent.PointerProperties();
+ properties.id = 0;
+ properties.toolType = Configurator.getInstance().getToolType();
+
+ MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
+ coords.pressure = 1;
+ coords.size = 1;
+ coords.x = x;
+ coords.y = y;
+
+ return MotionEvent.obtain(downTime, eventTime, action, 1,
+ new MotionEvent.PointerProperties[]{properties},
+ new MotionEvent.PointerCoords[]{coords},
+ 0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
+ }
+
+ void sendPointer(long downTime, long currentTime, int action, Point point) {
+ final MotionEvent event = getMotionEvent(downTime, currentTime, action, point.x, point.y);
+ mInstrumentation.getUiAutomation().injectInputEvent(event, true);
+ event.recycle();
+ }
+
+ void movePointer(long downTime, long duration, Point from, Point to) {
+ final Point point = new Point();
+ for (; ; ) {
+ sleep(16);
+
+ final long currentTime = SystemClock.uptimeMillis();
+ final float progress = (currentTime - downTime) / (float) duration;
+ if (progress > 1) return;
+
+ point.x = from.x + (int) (progress * (to.x - from.x));
+ point.y = from.y + (int) (progress * (to.y - from.y));
+
+ sendPointer(downTime, currentTime, MotionEvent.ACTION_MOVE, point);
+ }
+ }
+
+ static void sleep(int duration) {
+ try {
+ Thread.sleep(duration);
+ } catch (InterruptedException e) {
+ }
+ }
}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index e3ef74a..7d97acd 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -136,6 +136,8 @@
*/
public void flingForward() {
final UiObject2 workspace = verifyActiveContainer();
+ final int margin = (int) (50 * mLauncher.getDisplayDensity()) + 1;
+ workspace.setGestureMargins(0, 0, margin, 0);
workspace.fling(Direction.RIGHT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
mLauncher.waitForIdle();
verifyActiveContainer();
@@ -147,6 +149,8 @@
*/
public void flingBackward() {
final UiObject2 workspace = verifyActiveContainer();
+ final int margin = (int) (50 * mLauncher.getDisplayDensity()) + 1;
+ workspace.setGestureMargins(margin, 0, 0, 0);
workspace.fling(Direction.LEFT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
mLauncher.waitForIdle();
verifyActiveContainer();