Merge "Close taskbar all apps on popup notification and app info shortcut tap." into tm-dev
diff --git a/Android.bp b/Android.bp
index f5a38b4..5a153a7 100644
--- a/Android.bp
+++ b/Android.bp
@@ -208,6 +208,8 @@
srcs: [
"ext_tests/src/**/*.java",
"ext_tests/src/**/*.kt",
+ "quickstep/ext_tests/src/**/*.java",
+ "quickstep/ext_tests/src/**/*.kt",
],
}
@@ -224,6 +226,21 @@
],
}
+// Common source files used to build go launcher
+filegroup {
+ name: "launcher-go-src-no-build-config",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ "quickstep/src/**/*.java",
+ "quickstep/src/**/*.kt",
+ "go/src/**/*.java",
+ "go/src/**/*.kt",
+ "go/quickstep/src/**/*.java",
+ "go/quickstep/src/**/*.kt",
+ ],
+}
+
// Proguard files for Launcher3
filegroup {
name: "launcher-proguard-rules",
diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
index 02206c0..d16e12c 100644
--- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
+++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
@@ -29,8 +29,10 @@
import androidx.annotation.Keep;
import androidx.annotation.Nullable;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.ShortcutAndWidgetContainer;
import java.util.ArrayList;
import java.util.Collection;
@@ -205,6 +207,32 @@
}
}
+ case TestProtocol.REQUEST_USE_TEST_WORKSPACE_LAYOUT: {
+ useTestWorkspaceLayout(true);
+ return response;
+ }
+
+ case TestProtocol.REQUEST_USE_DEFAULT_WORKSPACE_LAYOUT: {
+ useTestWorkspaceLayout(false);
+ return response;
+ }
+
+ case TestProtocol.REQUEST_HOTSEAT_ICON_NAMES: {
+ return getLauncherUIProperty(Bundle::putStringArrayList, l -> {
+ ShortcutAndWidgetContainer hotseatIconsContainer =
+ l.getHotseat().getShortcutsAndWidgets();
+ ArrayList<String> hotseatIconNames = new ArrayList<>();
+
+ for (int i = 0; i < hotseatIconsContainer.getChildCount(); i++) {
+ // Use unchecked cast to catch changes in hotseat layout
+ BubbleTextView icon = (BubbleTextView) hotseatIconsContainer.getChildAt(i);
+ hotseatIconNames.add((String) icon.getText());
+ }
+
+ return hotseatIconNames;
+ });
+ }
+
case TestProtocol.REQUEST_GET_ACTIVITIES_CREATED_COUNT: {
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, sActivitiesCreatedCount);
return response;
@@ -223,4 +251,15 @@
return super.call(method, arg, extras);
}
}
+
+ private void useTestWorkspaceLayout(boolean useTestWorkspaceLayout) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ LauncherSettings.Settings.call(mContext.getContentResolver(), useTestWorkspaceLayout
+ ? LauncherSettings.Settings.METHOD_SET_USE_TEST_WORKSPACE_LAYOUT_FLAG
+ : LauncherSettings.Settings.METHOD_CLEAR_USE_TEST_WORKSPACE_LAYOUT_FLAG);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
}
diff --git a/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java b/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java
new file mode 100644
index 0000000..e5f0295
--- /dev/null
+++ b/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2022 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 static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.taskbar.LauncherTaskbarUIController;
+import com.android.launcher3.testing.DebugTestInformationHandler;
+import com.android.launcher3.testing.TestProtocol;
+
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Class to handle requests from tests, including debug ones, to Quickstep Launcher builds.
+ */
+public abstract class DebugQuickstepTestInformationHandler extends QuickstepTestInformationHandler {
+
+ private final DebugTestInformationHandler mDebugTestInformationHandler;
+
+ public DebugQuickstepTestInformationHandler(Context context) {
+ super(context);
+ mDebugTestInformationHandler = new DebugTestInformationHandler(context);
+ }
+
+ @Override
+ public Bundle call(String method, String arg, @Nullable Bundle extras) {
+ Bundle response = new Bundle();
+ switch (method) {
+ case TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING:
+ runOnUIThread(l -> {
+ enableManualTaskbarStashing(l, true);
+ });
+ return response;
+
+ case TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING:
+ runOnUIThread(l -> {
+ enableManualTaskbarStashing(l, false);
+ });
+ return response;
+
+ case TestProtocol.REQUEST_UNSTASH_TASKBAR_IF_STASHED:
+ runOnUIThread(l -> {
+ enableManualTaskbarStashing(l, true);
+
+ BaseQuickstepLauncher quickstepLauncher = (BaseQuickstepLauncher) l;
+ LauncherTaskbarUIController taskbarUIController =
+ quickstepLauncher.getTaskbarUIController();
+
+ // Allow null-pointer to catch illegal states.
+ taskbarUIController.unstashTaskbarIfStashed();
+
+ enableManualTaskbarStashing(l, false);
+ });
+ return response;
+
+ case TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT: {
+ final Resources resources = mContext.getResources();
+ response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size));
+ return response;
+ }
+
+ default:
+ response = super.call(method, arg, extras);
+ if (response != null) return response;
+ return mDebugTestInformationHandler.call(method, arg, extras);
+ }
+ }
+
+ private void enableManualTaskbarStashing(Launcher launcher, boolean enable) {
+ BaseQuickstepLauncher quickstepLauncher = (BaseQuickstepLauncher) launcher;
+ LauncherTaskbarUIController taskbarUIController =
+ quickstepLauncher.getTaskbarUIController();
+
+ // Allow null-pointer to catch illegal states.
+ taskbarUIController.enableManualStashingForTests(enable);
+ }
+
+ /**
+ * Runs the given command on the UI thread.
+ */
+ private static void runOnUIThread(UIThreadCommand command) {
+ try {
+ MAIN_EXECUTOR.submit(() -> {
+ command.execute(Launcher.ACTIVITY_TRACKER.getCreatedActivity());
+ return null;
+ }).get();
+ } catch (ExecutionException | InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private interface UIThreadCommand {
+
+ void execute(Launcher launcher);
+ }
+}
+
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 9a70146..f14e2a2 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -111,6 +111,7 @@
import com.android.launcher3.views.FloatingIconView;
import com.android.launcher3.views.ScrimView;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.quickstep.LauncherBackAnimationController;
import com.android.quickstep.RemoteAnimationTargets;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskViewUtils;
@@ -216,6 +217,7 @@
private RemoteAnimationFactory mWallpaperOpenTransitionRunner;
private RemoteTransitionCompat mLauncherOpenTransition;
+ private LauncherBackAnimationController mBackAnimationController;
private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
@@ -241,6 +243,8 @@
mDragLayerAlpha = mDragLayer.getAlphaProperty(ALPHA_INDEX_TRANSITIONS);
mHandler = new Handler(Looper.getMainLooper());
mDeviceProfile = mLauncher.getDeviceProfile();
+ mBackAnimationController = new LauncherBackAnimationController(
+ mDeviceProfile, mLauncher, this);
Resources res = mLauncher.getResources();
mContentScale = res.getFloat(R.dimen.content_scale);
@@ -1159,6 +1163,9 @@
mLauncherOpenTransition.addHomeOpenCheck(mLauncher.getComponentName());
SystemUiProxy.INSTANCE.getNoCreate().registerRemoteTransition(mLauncherOpenTransition);
}
+ if (mBackAnimationController != null) {
+ mBackAnimationController.registerBackCallbacks(mHandler);
+ }
}
public void onActivityDestroyed() {
@@ -1194,6 +1201,10 @@
mLauncherOpenTransition = null;
mWallpaperOpenTransitionRunner = null;
}
+ if (mBackAnimationController != null) {
+ mBackAnimationController.unregisterBackCallbacks();
+ mBackAnimationController = null;
+ }
}
private boolean launcherIsATargetWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
@@ -1346,8 +1357,9 @@
/**
* Closing animator that animates the window into its final location on the workspace.
*/
- private void getClosingWindowAnimators(AnimatorSet animation,
- RemoteAnimationTargetCompat[] targets, View launcherView, PointF velocityPxPerS) {
+ private RectFSpringAnim getClosingWindowAnimators(AnimatorSet animation,
+ RemoteAnimationTargetCompat[] targets, View launcherView, PointF velocityPxPerS,
+ RectF closingWindowStartRect) {
FloatingIconView floatingIconView = null;
FloatingWidgetView floatingWidget = null;
RectF targetRect = new RectF();
@@ -1379,8 +1391,7 @@
targetRect.set(getDefaultWindowTargetRect());
}
- final RectF startRect = new RectF(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx);
- RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mLauncher,
+ RectFSpringAnim anim = new RectFSpringAnim(closingWindowStartRect, targetRect, mLauncher,
mDeviceProfile);
// Hook up floating views to the closing window animators.
@@ -1414,7 +1425,7 @@
final float floatingWidgetAlpha = isTransluscent ? 0 : 1;
FloatingWidgetView finalFloatingWidget = floatingWidget;
- RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect,
+ RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect,
windowTargetBounds) {
@Override
public void onUpdate(RectF currentRectF, float progress) {
@@ -1438,6 +1449,7 @@
anim.start(mLauncher, velocityPxPerS);
}
});
+ return anim;
}
/**
@@ -1562,6 +1574,97 @@
}
/**
+ * Creates the {@link RectFSpringAnim} and {@link AnimatorSet} required to animate
+ * the transition.
+ */
+ public Pair<RectFSpringAnim, AnimatorSet> createWallpaperOpenAnimations(
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
+ boolean fromUnlock,
+ RectF startRect) {
+ AnimatorSet anim = null;
+ RectFSpringAnim rectFSpringAnim = null;
+
+ RemoteAnimationProvider provider = mRemoteAnimationProvider;
+ if (provider != null) {
+ anim = provider.createWindowAnimation(appTargets, wallpaperTargets);
+ }
+
+ if (anim == null) {
+ anim = new AnimatorSet();
+
+ final boolean launcherIsForceInvisibleOrOpening = mLauncher.isForceInvisible()
+ || launcherIsATargetWithMode(appTargets, MODE_OPENING);
+
+ View launcherView = findLauncherView(appTargets);
+ boolean playFallBackAnimation = (launcherView == null
+ && launcherIsForceInvisibleOrOpening)
+ || mLauncher.getWorkspace().isOverlayShown()
+ || hasMultipleTargetsWithMode(appTargets, MODE_CLOSING);
+
+ boolean playWorkspaceReveal = true;
+ boolean skipAllAppsScale = false;
+ if (fromUnlock) {
+ anim.play(getUnlockWindowAnimator(appTargets, wallpaperTargets));
+ } else if (ENABLE_BACK_SWIPE_HOME_ANIMATION.get()
+ && !playFallBackAnimation) {
+ // Use a fixed velocity to start the animation.
+ float velocityPxPerS = DynamicResource.provider(mLauncher)
+ .getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
+ PointF velocity = new PointF(0, -velocityPxPerS);
+ rectFSpringAnim = getClosingWindowAnimators(
+ anim, appTargets, launcherView, velocity, startRect);
+ if (!mLauncher.isInState(LauncherState.ALL_APPS)) {
+ anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
+ true /* animateOverviewScrim */, launcherView).getAnimators());
+ // We play StaggeredWorkspaceAnim as a part of the closing window animation.
+ playWorkspaceReveal = false;
+ } else {
+ // Skip scaling all apps, otherwise FloatingIconView will get wrong
+ // layout bounds.
+ skipAllAppsScale = true;
+ }
+ } else {
+ anim.play(getFallbackClosingWindowAnimators(appTargets));
+ }
+
+ // Normally, we run the launcher content animation when we are transitioning
+ // home, but if home is already visible, then we don't want to animate the
+ // contents of launcher unless we know that we are animating home as a result
+ // of the home button press with quickstep, which will result in launcher being
+ // started on touch down, prior to the animation home (and won't be in the
+ // targets list because it is already visible). In that case, we force
+ // invisibility on touch down, and only reset it after the animation to home
+ // is initialized.
+ if (launcherIsForceInvisibleOrOpening) {
+ addCujInstrumentation(
+ anim, InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
+ // Only register the content animation for cancellation when state changes
+ mLauncher.getStateManager().setCurrentAnimation(anim);
+
+ if (mLauncher.isInState(LauncherState.ALL_APPS)) {
+ Pair<AnimatorSet, Runnable> contentAnimator =
+ getLauncherContentAnimator(false, LAUNCHER_RESUME_START_DELAY,
+ skipAllAppsScale);
+ anim.play(contentAnimator.first);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ contentAnimator.second.run();
+ }
+ });
+ } else {
+ if (playWorkspaceReveal) {
+ anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators());
+ }
+ }
+ }
+ }
+
+ return new Pair(rectFSpringAnim, anim);
+ }
+
+ /**
* Remote animation runner for animation from the app to Launcher, including recents.
*/
protected class WallpaperOpenLauncherAnimationRunner implements RemoteAnimationFactory {
@@ -1601,84 +1704,12 @@
mLauncher.getStateManager().moveToRestState();
}
- AnimatorSet anim = null;
- RemoteAnimationProvider provider = mRemoteAnimationProvider;
- if (provider != null) {
- anim = provider.createWindowAnimation(appTargets, wallpaperTargets);
- }
-
- if (anim == null) {
- anim = new AnimatorSet();
-
- final boolean launcherIsForceInvisibleOrOpening = mLauncher.isForceInvisible()
- || launcherIsATargetWithMode(appTargets, MODE_OPENING);
-
- View launcherView = findLauncherView(appTargets);
- boolean playFallBackAnimation = (launcherView == null
- && launcherIsForceInvisibleOrOpening)
- || mLauncher.getWorkspace().isOverlayShown()
- || hasMultipleTargetsWithMode(appTargets, MODE_CLOSING);
-
- boolean playWorkspaceReveal = true;
- boolean skipAllAppsScale = false;
- if (mFromUnlock) {
- anim.play(getUnlockWindowAnimator(appTargets, wallpaperTargets));
- } else if (ENABLE_BACK_SWIPE_HOME_ANIMATION.get()
- && !playFallBackAnimation) {
- // Use a fixed velocity to start the animation.
- float velocityPxPerS = DynamicResource.provider(mLauncher)
- .getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
- PointF velocity = new PointF(0, -velocityPxPerS);
- getClosingWindowAnimators(anim, appTargets, launcherView, velocity);
- if (!mLauncher.isInState(LauncherState.ALL_APPS)) {
- anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
- true /* animateOverviewScrim */, launcherView).getAnimators());
- // We play StaggeredWorkspaceAnim as a part of the closing window animation.
- playWorkspaceReveal = false;
- } else {
- // Skip scaling all apps, otherwise FloatingIconView will get wrong
- // layout bounds.
- skipAllAppsScale = true;
- }
- } else {
- anim.play(getFallbackClosingWindowAnimators(appTargets));
- }
-
- // Normally, we run the launcher content animation when we are transitioning
- // home, but if home is already visible, then we don't want to animate the
- // contents of launcher unless we know that we are animating home as a result
- // of the home button press with quickstep, which will result in launcher being
- // started on touch down, prior to the animation home (and won't be in the
- // targets list because it is already visible). In that case, we force
- // invisibility on touch down, and only reset it after the animation to home
- // is initialized.
- if (launcherIsForceInvisibleOrOpening) {
- addCujInstrumentation(
- anim, InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
- // Only register the content animation for cancellation when state changes
- mLauncher.getStateManager().setCurrentAnimation(anim);
-
- if (mLauncher.isInState(LauncherState.ALL_APPS)) {
- Pair<AnimatorSet, Runnable> contentAnimator =
- getLauncherContentAnimator(false, LAUNCHER_RESUME_START_DELAY,
- skipAllAppsScale);
- anim.play(contentAnimator.first);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- contentAnimator.second.run();
- }
- });
- } else {
- if (playWorkspaceReveal) {
- anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators());
- }
- }
- }
- }
+ Pair<RectFSpringAnim, AnimatorSet> pair = createWallpaperOpenAnimations(
+ appTargets, wallpaperTargets, mFromUnlock,
+ new RectF(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx));
mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
- result.setAnimation(anim, mLauncher);
+ result.setAnimation(pair.second, mLauncher);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 768a348..793d987 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -27,6 +27,7 @@
import android.view.WindowManagerGlobal;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
@@ -118,6 +119,24 @@
}
/**
+ * Enables manual taskbar stashing. This method should only be used for tests that need to
+ * stash/unstash the taskbar.
+ */
+ @VisibleForTesting
+ public void enableManualStashingForTests(boolean enableManualStashing) {
+ mControllers.taskbarStashController.enableManualStashingForTests(enableManualStashing);
+ }
+
+ /**
+ * Unstashes the Taskbar if it is stashed. This method should only be used to unstash the
+ * taskbar at the end of a test.
+ */
+ @VisibleForTesting
+ public void unstashTaskbarIfStashed() {
+ mControllers.taskbarStashController.onLongPressToUnstashTaskbar();
+ }
+
+ /**
* Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
*/
public void onLauncherResumedOrPaused(boolean isResumed) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 5387b1a..6c74ae3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -71,6 +71,8 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.NavigationMode;
@@ -646,12 +648,16 @@
Toast.makeText(this, R.string.safemode_shortcut_error,
Toast.LENGTH_SHORT).show();
} else if (info.isPromise()) {
+ TestLogging.recordEvent(
+ TestProtocol.SEQUENCE_MAIN, "start: taskbarPromiseIcon");
intent = new PackageManagerHelper(this)
.getMarketIntent(info.getTargetPackage())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} else if (info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ TestLogging.recordEvent(
+ TestProtocol.SEQUENCE_MAIN, "start: taskbarDeepShortcut");
String id = info.getDeepShortcutId();
String packageName = intent.getPackage();
getSystemService(LauncherApps.class)
@@ -680,6 +686,7 @@
Intent intent = new Intent(info.getIntent())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: taskbarAppIcon");
if (info.user.equals(Process.myUserHandle())) {
// TODO(b/216683257): Use startActivityForResult for search results that require it.
startActivity(intent);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index af98b7f..e2ab443 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -63,6 +63,8 @@
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.systemui.shared.recents.model.Task;
@@ -128,7 +130,7 @@
if (!(view instanceof BubbleTextView)) {
return false;
}
-
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onTaskbarItemLongClick");
BubbleTextView btv = (BubbleTextView) view;
mActivity.onDragStart();
btv.post(() -> {
@@ -164,24 +166,7 @@
dragLayerY += dragRect.top;
DragOptions dragOptions = new DragOptions();
- dragOptions.preDragCondition = new DragOptions.PreDragCondition() {
- private DragView mDragView;
-
- @Override
- public boolean shouldStartDrag(double distanceDragged) {
- return mDragView != null && mDragView.isAnimationFinished();
- }
-
- @Override
- public void onPreDragStart(DropTarget.DragObject dragObject) {
- mDragView = dragObject.dragView;
- }
-
- @Override
- public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
- mDragView = null;
- }
- };
+ dragOptions.preDragCondition = null;
if (FeatureFlags.ENABLE_TASKBAR_POPUP_MENU.get()) {
PopupContainerWithArrow<BaseTaskbarContext> popupContainer =
mControllers.taskbarPopupController.showForIcon(btv);
@@ -189,6 +174,32 @@
dragOptions.preDragCondition = popupContainer.createPreDragCondition(false);
}
}
+ if (dragOptions.preDragCondition == null) {
+ dragOptions.preDragCondition = new DragOptions.PreDragCondition() {
+ private DragView mDragView;
+
+ @Override
+ public boolean shouldStartDrag(double distanceDragged) {
+ return mDragView != null && mDragView.isAnimationFinished();
+ }
+
+ @Override
+ public void onPreDragStart(DropTarget.DragObject dragObject) {
+ mDragView = dragObject.dragView;
+
+ if (FeatureFlags.ENABLE_TASKBAR_POPUP_MENU.get()
+ && !shouldStartDrag(0)) {
+ // Immediately close the popup menu.
+ mDragView.setOnAnimationEndCallback(() -> callOnDragStart());
+ }
+ }
+
+ @Override
+ public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
+ mDragView = null;
+ }
+ };
+ }
return startDrag(
drawable,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index ea4fe34..f9c8062 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -145,6 +145,7 @@
});
// TODO (b/198438631): configure for taskbar/context
container.setPopupItemDragHandler(new TaskbarPopupItemDragHandler());
+ mControllers.taskbarDragController.addDragListener(container);
container.populateAndShow(icon,
mPopupDataProvider.getShortcutCountForItem(item),
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 473be9e..f9a282b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -136,6 +136,8 @@
private boolean mIsSystemGestureInProgress;
private boolean mIsImeShowing;
+ private boolean mEnableManualStashingForTests = false;
+
// Evaluate whether the handle should be stashed
private final StatePropertyHolder mStatePropertyHolder = new StatePropertyHolder(
flags -> {
@@ -199,12 +201,15 @@
*/
protected boolean supportsManualStashing() {
return supportsVisualStashing()
- && (!Utilities.IS_RUNNING_IN_TEST_HARNESS || supportsStashingForTests());
+ && (!Utilities.IS_RUNNING_IN_TEST_HARNESS || mEnableManualStashingForTests);
}
- private boolean supportsStashingForTests() {
- // TODO: enable this for tests that specifically check stash/unstash behavior.
- return false;
+ /**
+ * Enables support for manual stashing. This should only be used to add this functionality
+ * to Launcher specific tests.
+ */
+ public void enableManualStashingForTests(boolean enableManualStashing) {
+ mEnableManualStashingForTests = enableManualStashing;
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
index 23da499..22fffdf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.graphics.Insets;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.View;
import android.view.WindowInsets;
@@ -38,6 +39,8 @@
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarDragController;
import com.android.launcher3.taskbar.TaskbarStashController;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
@@ -180,6 +183,12 @@
}
@Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
+ return super.dispatchTouchEvent(ev);
+ }
+
+ @Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK) {
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
index 044459e..9fca8eb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
@@ -39,6 +39,8 @@
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarControllers;
import com.android.launcher3.taskbar.TaskbarSharedState;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import java.util.List;
import java.util.Optional;
@@ -62,6 +64,13 @@
private final TaskbarAllAppsProxyView mProxyView;
private final LayoutParams mLayoutParams;
+ private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
+ @Override
+ public void onTaskStackChanged() {
+ mProxyView.close(false);
+ }
+ };
+
private TaskbarControllers mControllers;
private TaskbarSharedState mSharedState;
/** Window context for all apps if it is open. */
@@ -139,6 +148,7 @@
mControllers.taskbarStashController);
mAllAppsContext.getDragController().init(mControllers);
mTaskbarContext.addOnDeviceProfileChangeListener(this);
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
Optional.ofNullable(mAllAppsContext.getSystemService(WindowManager.class))
.ifPresent(m -> m.addView(mAllAppsContext.getDragLayer(), mLayoutParams));
@@ -172,6 +182,7 @@
/** Destroys the controller and any All Apps window if present. */
public void onDestroy() {
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
mTaskbarContext.removeOnDeviceProfileChangeListener(this);
Optional.ofNullable(mAllAppsContext)
.map(c -> c.getSystemService(WindowManager.class))
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 6584f88..9686510 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -362,10 +362,9 @@
/** Gets the space that the overview actions will take, including bottom margin. */
private int getOverviewActionsHeight(Context context, DeviceProfile dp) {
- Resources res = context.getResources();
return OverviewActionsView.getOverviewActionsBottomMarginPx(getNavigationMode(context), dp)
+ OverviewActionsView.getOverviewActionsTopMarginPx(getNavigationMode(context), dp)
- + res.getDimensionPixelSize(R.dimen.overview_actions_height);
+ + dp.overviewActionsHeight;
}
/**
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
new file mode 100644
index 0000000..7abcbdb
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2022 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 static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
+import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
+import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Handler;
+import android.util.Log;
+import android.util.MathUtils;
+import android.util.Pair;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.window.BackEvent;
+import android.window.IOnBackInvokedCallback;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.QuickstepTransitionManager;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.quickstep.util.RectFSpringAnim;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+/**
+ * Controls the animation of swiping back and returning to launcher.
+ *
+ * This is a two part animation. The first part is an animation that tracks gesture location to
+ * scale and move the leaving app window. Once the gesture is committed, the second part takes over
+ * the app window and plays the rest of app close transitions in one go.
+ *
+ * This animation is used only for apps that enable back dispatching via
+ * {@link android.view.OnBackInvokedDispatcher}. The controller registers
+ * an {@link IOnBackInvokedCallback} with WM Shell and receives back dispatches when a back
+ * navigation to launcher starts.
+ *
+ * Apps using the legacy back dispatching will keep triggering the WALLPAPER_OPEN remote
+ * transition registered in {@link QuickstepTransitionManager}.
+ *
+ */
+public class LauncherBackAnimationController {
+ private static final int CANCEL_TRANSITION_DURATION = 150;
+ private static final String TAG = "LauncherBackAnimationController";
+ private final DeviceProfile mDeviceProfile;
+ private final QuickstepTransitionManager mQuickstepTransitionManager;
+ private final Matrix mTransformMatrix = new Matrix();
+ private final RectF mTargetRectF = new RectF();
+ private final RectF mStartRectF = new RectF();
+ private final RectF mCurrentRect = new RectF();
+ private final BaseQuickstepLauncher mLauncher;
+ private final int mWindowScaleMarginX;
+ private final int mWindowScaleMarginY;
+ private final float mWindowScaleEndCornerRadius;
+ private final float mWindowScaleStartCornerRadius;
+
+ private RemoteAnimationTargetCompat mBackTarget;
+ private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+ private boolean mSpringAnimationInProgress = false;
+ private boolean mAnimatorSetInProgress = false;
+ @BackEvent.SwipeEdge
+ private int mSwipeEdge;
+ private float mBackProgress = 0;
+ private boolean mBackInProgress = false;
+
+ public LauncherBackAnimationController(
+ DeviceProfile deviceProfile,
+ BaseQuickstepLauncher launcher,
+ QuickstepTransitionManager quickstepTransitionManager) {
+ mDeviceProfile = deviceProfile;
+ mLauncher = launcher;
+ mQuickstepTransitionManager = quickstepTransitionManager;
+ mWindowScaleEndCornerRadius = QuickStepContract.supportsRoundedCornersOnWindows(
+ mLauncher.getResources())
+ ? mLauncher.getResources().getDimensionPixelSize(
+ R.dimen.swipe_back_window_corner_radius)
+ : 0;
+ mWindowScaleStartCornerRadius = QuickStepContract.getWindowCornerRadius(mLauncher);
+ mWindowScaleMarginX = mLauncher.getResources().getDimensionPixelSize(
+ R.dimen.swipe_back_window_scale_x_margin);
+ mWindowScaleMarginY = mLauncher.getResources().getDimensionPixelSize(
+ R.dimen.swipe_back_window_scale_y_margin);
+ }
+
+ /**
+ * Registers {@link IOnBackInvokedCallback} to receive back dispatches from shell.
+ * @param handler Handler to the thread to run the animations on.
+ */
+ public void registerBackCallbacks(Handler handler) {
+ SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
+ if (systemUiProxy == null) {
+ Log.e(TAG, "SystemUiProxy is null. Skip registering back invocation callbacks");
+ return;
+ }
+ systemUiProxy.setBackToLauncherCallback(
+ new IOnBackInvokedCallback.Stub() {
+ @Override
+ public void onBackCancelled() {
+ handler.post(() -> resetPositionAnimated());
+ }
+
+ @Override
+ public void onBackInvoked() {
+ handler.post(() -> startTransition());
+ }
+
+ @Override
+ public void onBackProgressed(BackEvent backEvent) {
+ mBackProgress = backEvent.getProgress();
+ // TODO: Update once the interpolation curve spec is finalized.
+ mBackProgress =
+ 1 - (1 - mBackProgress) * (1 - mBackProgress) * (1
+ - mBackProgress);
+ if (!mBackInProgress) {
+ startBack(backEvent);
+ } else {
+ updateBackProgress(mBackProgress);
+ }
+ }
+
+ public void onBackStarted() { }
+ });
+ }
+
+ private void resetPositionAnimated() {
+ ValueAnimator cancelAnimator = ValueAnimator.ofFloat(mBackProgress, 0);
+ cancelAnimator.setDuration(CANCEL_TRANSITION_DURATION);
+ cancelAnimator.addUpdateListener(
+ animation -> {
+ updateBackProgress((float) animation.getAnimatedValue());
+ });
+ cancelAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ finishAnimation();
+ }
+ });
+ cancelAnimator.start();
+ }
+
+ /** Unregisters the back to launcher callback in shell. */
+ public void unregisterBackCallbacks() {
+ SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
+ if (systemUiProxy != null) {
+ systemUiProxy.clearBackToLauncherCallback();
+ }
+ }
+
+ private void startBack(BackEvent backEvent) {
+ mBackInProgress = true;
+ RemoteAnimationTarget appTarget = backEvent.getDepartingAnimationTarget();
+
+ if (appTarget == null) {
+ return;
+ }
+
+ mTransaction.show(appTarget.leash).apply();
+ mTransaction.setAnimationTransaction();
+ mBackTarget = new RemoteAnimationTargetCompat(appTarget);
+ mSwipeEdge = backEvent.getSwipeEdge();
+ float screenWidth = mDeviceProfile.widthPx;
+ float screenHeight = mDeviceProfile.heightPx;
+ float targetHeight = screenHeight - 2 * mWindowScaleMarginY;
+ float targetWidth = targetHeight * screenWidth / screenHeight;
+ float left;
+ if (mSwipeEdge == BackEvent.EDGE_LEFT) {
+ left = screenWidth - targetWidth - mWindowScaleMarginX;
+ } else {
+ left = mWindowScaleMarginX;
+ }
+ float top = mWindowScaleMarginY;
+ // TODO(b/218916755): Offset start rectangle in multiwindow mode.
+ mStartRectF.set(0, 0, screenWidth, screenHeight);
+ mTargetRectF.set(left, top, targetWidth + left, targetHeight + top);
+ }
+
+ private void updateBackProgress(float progress) {
+ if (mBackTarget == null) {
+ return;
+ }
+
+ mCurrentRect.set(
+ MathUtils.lerp(mStartRectF.left, mTargetRectF.left, progress),
+ MathUtils.lerp(mStartRectF.top, mTargetRectF.top, progress),
+ MathUtils.lerp(mStartRectF.right, mTargetRectF.right, progress),
+ MathUtils.lerp(mStartRectF.bottom, mTargetRectF.bottom, progress));
+ SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder builder =
+ new SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder(mBackTarget.leash);
+
+ Rect currentRect = new Rect();
+ mCurrentRect.round(currentRect);
+
+ // Scale the target window to match the currentRectF.
+ final float scale = mCurrentRect.width() / mStartRectF.width();
+ mTransformMatrix.reset();
+ mTransformMatrix.setScale(scale, scale);
+ mTransformMatrix.postTranslate(mCurrentRect.left, mCurrentRect.top);
+ Rect startRect = new Rect();
+ mStartRectF.round(startRect);
+ float cornerRadius = Utilities.mapRange(
+ progress, mWindowScaleStartCornerRadius, mWindowScaleEndCornerRadius);
+ builder.withMatrix(mTransformMatrix)
+ .withWindowCrop(startRect)
+ .withCornerRadius(cornerRadius);
+ SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams = builder.build();
+
+ if (surfaceParams.surface.isValid()) {
+ surfaceParams.applyTo(mTransaction);
+ }
+ mTransaction.apply();
+ }
+
+ private void startTransition() {
+ if (mBackTarget == null) {
+ // Trigger transition system instead of custom transition animation.
+ finishAnimation();
+ return;
+ }
+ if (mLauncher.isDestroyed()) {
+ return;
+ }
+ // TODO: Catch the moment when launcher becomes visible after the top app un-occludes
+ // launcher and start animating afterwards. Currently we occasionally get a flicker from
+ // animating when launcher is still invisible.
+ if (mLauncher.hasSomeInvisibleFlag(PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION)) {
+ mLauncher.addForceInvisibleFlag(INVISIBLE_BY_PENDING_FLAGS);
+ mLauncher.getStateManager().moveToRestState();
+ }
+
+ Pair<RectFSpringAnim, AnimatorSet> pair =
+ mQuickstepTransitionManager.createWallpaperOpenAnimations(
+ new RemoteAnimationTargetCompat[]{mBackTarget},
+ new RemoteAnimationTargetCompat[]{},
+ false /* fromUnlock */,
+ mCurrentRect);
+ startTransitionAnimations(pair.first, pair.second);
+ mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
+ }
+
+ private void finishAnimation() {
+ mBackTarget = null;
+ mBackInProgress = false;
+ mBackProgress = 0;
+ mSwipeEdge = BackEvent.EDGE_LEFT;
+ mTransformMatrix.reset();
+ mTargetRectF.setEmpty();
+ mCurrentRect.setEmpty();
+ mStartRectF.setEmpty();
+ mAnimatorSetInProgress = false;
+ mSpringAnimationInProgress = false;
+ SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
+ if (systemUiProxy != null) {
+ SystemUiProxy.INSTANCE.getNoCreate().onBackToLauncherAnimationFinished();
+ }
+ }
+
+ private void startTransitionAnimations(RectFSpringAnim springAnim, AnimatorSet anim) {
+ mAnimatorSetInProgress = anim != null;
+ mSpringAnimationInProgress = springAnim != null;
+ if (springAnim != null) {
+ springAnim.addAnimatorListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mSpringAnimationInProgress = false;
+ tryFinishBackAnimation();
+ }
+ }
+ );
+ }
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimatorSetInProgress = false;
+ tryFinishBackAnimation();
+ }
+ });
+ anim.start();
+ }
+
+ private void tryFinishBackAnimation() {
+ if (!mSpringAnimationInProgress && !mAnimatorSetInProgress) {
+ finishAnimation();
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 667ea14..5ef89d3 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -39,6 +39,7 @@
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
+import android.window.IOnBackInvokedCallback;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.Info;
@@ -50,6 +51,7 @@
import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController;
import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
import com.android.systemui.shared.system.smartspace.SmartspaceState;
+import com.android.wm.shell.back.IBackAnimation;
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.pip.IPip;
import com.android.wm.shell.pip.IPipAnimationListener;
@@ -82,6 +84,7 @@
private IShellTransitions mShellTransitions;
private IStartingWindow mStartingWindow;
private IRecentTasks mRecentTasks;
+ private IBackAnimation mBackAnimation;
private final DeathRecipient mSystemUiProxyDeathRecipient = () -> {
MAIN_EXECUTOR.execute(() -> clearProxy());
};
@@ -96,6 +99,7 @@
private ILauncherUnlockAnimationController mPendingLauncherUnlockAnimationController;
private IRecentTasksListener mRecentTasksListener;
private final ArrayList<RemoteTransitionCompat> mRemoteTransitions = new ArrayList<>();
+ private IOnBackInvokedCallback mBackToLauncherCallback;
// Used to dedupe calls to SystemUI
private int mLastShelfHeight;
@@ -162,8 +166,8 @@
public void setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen,
IOneHanded oneHanded, IShellTransitions shellTransitions,
IStartingWindow startingWindow, IRecentTasks recentTasks,
- ISysuiUnlockAnimationController sysuiUnlockAnimationController) {
-
+ ISysuiUnlockAnimationController sysuiUnlockAnimationController,
+ IBackAnimation backAnimation) {
unlinkToDeath();
mSystemUiProxy = proxy;
mPip = pip;
@@ -173,6 +177,7 @@
mStartingWindow = startingWindow;
mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
mRecentTasks = recentTasks;
+ mBackAnimation = backAnimation;
linkToDeath();
// re-attach the listeners once missing due to setProxy has not been initialized yet.
if (mPipAnimationListener != null && mPip != null) {
@@ -195,6 +200,9 @@
if (mRecentTasksListener != null && mRecentTasks != null) {
registerRecentTasksListener(mRecentTasksListener);
}
+ if (mBackAnimation != null && mBackToLauncherCallback != null) {
+ setBackToLauncherCallback(mBackToLauncherCallback);
+ }
if (mPendingSetNavButtonAlpha != null) {
mPendingSetNavButtonAlpha.run();
@@ -203,7 +211,7 @@
}
public void clearProxy() {
- setProxy(null, null, null, null, null, null, null, null);
+ setProxy(null, null, null, null, null, null, null, null, null);
}
// TODO(141886704): Find a way to remove this
@@ -822,6 +830,50 @@
mRecentTasksListener = null;
}
+ //
+ // Back navigation transitions
+ //
+
+ /** Sets the launcher {@link android.window.IOnBackInvokedCallback} to shell */
+ public void setBackToLauncherCallback(IOnBackInvokedCallback callback) {
+ mBackToLauncherCallback = callback;
+ if (mBackAnimation == null) {
+ return;
+ }
+ try {
+ mBackAnimation.setBackToLauncherCallback(callback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed call setBackToLauncherCallback", e);
+ }
+ }
+
+ /** Clears the previously registered {@link IOnBackInvokedCallback}. */
+ public void clearBackToLauncherCallback() {
+ mBackToLauncherCallback = null;
+ if (mBackAnimation == null) {
+ return;
+ }
+ try {
+ mBackAnimation.clearBackToLauncherCallback();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed call clearBackToLauncherCallback", e);
+ }
+ }
+
+ /**
+ * Notifies shell that all back to launcher animations have finished (including the transition
+ * that plays after the gesture is committed and before the app is closed.
+ */
+ public void onBackToLauncherAnimationFinished() {
+ if (mBackAnimation != null) {
+ try {
+ mBackAnimation.onBackToLauncherAnimationFinished();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call onBackAnimationFinished", e);
+ }
+ }
+ }
+
public ArrayList<GroupedRecentTaskInfo> getRecentTasks(int numTasks, int userId) {
if (mRecentTasks != null) {
try {
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 021048a..ff67b09 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -26,6 +26,7 @@
import static com.android.quickstep.GestureState.DEFAULT_STATE;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_BACK_ANIMATION;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
@@ -106,6 +107,7 @@
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
import com.android.systemui.shared.tracing.ProtoTraceable;
+import com.android.wm.shell.back.IBackAnimation;
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.pip.IPip;
import com.android.wm.shell.recents.IRecentTasks;
@@ -166,10 +168,12 @@
bundle.getBinder(KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER));
IRecentTasks recentTasks = IRecentTasks.Stub.asInterface(
bundle.getBinder(KEY_EXTRA_RECENT_TASKS));
+ IBackAnimation backAnimation = IBackAnimation.Stub.asInterface(
+ bundle.getBinder(KEY_EXTRA_SHELL_BACK_ANIMATION));
MAIN_EXECUTOR.execute(() -> {
SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip,
splitscreen, onehanded, shellTransitions, startingWindow, recentTasks,
- launcherUnlockAnimationController);
+ launcherUnlockAnimationController, backAnimation);
TouchInteractionService.this.initInputMonitor();
preloadOverview(true /* fromInit */);
});
diff --git a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialFragment.java
index b797f0c..b3f2354 100644
--- a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialFragment.java
@@ -15,13 +15,21 @@
*/
package com.android.quickstep.interaction;
+import android.content.SharedPreferences;
import android.view.MotionEvent;
import android.view.View;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.quickstep.interaction.TutorialController.TutorialType;
/** Shows the Home gesture interactive tutorial. */
public class AssistantGestureTutorialFragment extends TutorialFragment {
+
+ protected AssistantGestureTutorialFragment(
+ SharedPreferences sharedPrefs, StatsLogManager statsLogManager) {
+ super(sharedPrefs, statsLogManager);
+ }
+
@Override
TutorialController createController(TutorialType type) {
return new AssistantGestureTutorialController(this, type);
@@ -39,4 +47,14 @@
}
return super.onTouch(view, motionEvent);
}
+
+ @Override
+ void logTutorialStepShown() {
+ // No-Op: tutorial step not currently shown to users
+ }
+
+ @Override
+ void logTutorialStepCompleted() {
+ // No-Op: tutorial step not currently shown to users
+ }
}
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java
index f54734d..e7ed0b4 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java
@@ -19,12 +19,14 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
+import android.content.SharedPreferences;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
import com.android.launcher3.R;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.quickstep.interaction.TutorialController.TutorialType;
import java.util.ArrayList;
@@ -32,6 +34,11 @@
/** Shows the Back gesture interactive tutorial. */
public class BackGestureTutorialFragment extends TutorialFragment {
+ protected BackGestureTutorialFragment(
+ SharedPreferences sharedPrefs, StatsLogManager statsLogManager) {
+ super(sharedPrefs, statsLogManager);
+ }
+
@Nullable
@Override
Integer getEdgeAnimationResId() {
@@ -117,4 +124,16 @@
}
return super.onTouch(view, motionEvent);
}
+
+ @Override
+ void logTutorialStepShown() {
+ mStatsLogManager.logger().log(
+ StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_BACK_STEP_SHOWN);
+ }
+
+ @Override
+ void logTutorialStepCompleted() {
+ mStatsLogManager.logger().log(
+ StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_BACK_STEP_COMPLETED);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index c2524b1..002e8b7 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep.interaction;
+import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Bundle;
@@ -28,6 +29,8 @@
import androidx.fragment.app.FragmentActivity;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.quickstep.interaction.TutorialController.TutorialType;
import java.util.List;
@@ -46,17 +49,26 @@
private int mCurrentStep;
private int mNumSteps;
+ private SharedPreferences mSharedPrefs;
+ private StatsLogManager mStatsLogManager;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.gesture_tutorial_activity);
+ mSharedPrefs = Utilities.getPrefs(this);
+ mStatsLogManager = StatsLogManager.newInstance(getApplicationContext());
+
Bundle args = savedInstanceState == null ? getIntent().getExtras() : savedInstanceState;
mTutorialSteps = getTutorialSteps(args);
mCurrentTutorialStep = mTutorialSteps[mCurrentStep - 1];
mFragment = TutorialFragment.newInstance(
- mCurrentTutorialStep, args.getBoolean(KEY_GESTURE_COMPLETE, false));
+ mCurrentTutorialStep,
+ args.getBoolean(KEY_GESTURE_COMPLETE, false),
+ mSharedPrefs,
+ mStatsLogManager);
getSupportFragmentManager().beginTransaction()
.add(R.id.gesture_tutorial_fragment_container, mFragment)
.commit();
@@ -105,13 +117,6 @@
}
/**
- * Closes the tutorial and this activity.
- */
- public void closeTutorial() {
- mFragment.closeTutorial();
- }
-
- /**
* Replaces the current TutorialFragment, continuing to the next tutorial step if there is one.
*
* If there is no following step, the tutorial is closed.
@@ -122,7 +127,8 @@
return;
}
mCurrentTutorialStep = mTutorialSteps[mCurrentStep];
- mFragment = TutorialFragment.newInstance(mCurrentTutorialStep, false);
+ mFragment = TutorialFragment.newInstance(
+ mCurrentTutorialStep, /* gestureComplete= */ false, mSharedPrefs, mStatsLogManager);
getSupportFragmentManager().beginTransaction()
.replace(R.id.gesture_tutorial_fragment_container, mFragment)
.runOnCommit(() -> mFragment.onAttachedToWindow())
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java
index 423e66f..e987d5a 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java
@@ -18,12 +18,14 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
+import android.content.SharedPreferences;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
import com.android.launcher3.R;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.quickstep.interaction.TutorialController.TutorialType;
import java.util.ArrayList;
@@ -31,6 +33,11 @@
/** Shows the Home gesture interactive tutorial. */
public class HomeGestureTutorialFragment extends TutorialFragment {
+ protected HomeGestureTutorialFragment(
+ SharedPreferences sharedPrefs, StatsLogManager statsLogManager) {
+ super(sharedPrefs, statsLogManager);
+ }
+
@Nullable
@Override
Integer getEdgeAnimationResId() {
@@ -99,4 +106,16 @@
releaseFeedbackAnimation();
return super.onTouch(view, motionEvent);
}
+
+ @Override
+ void logTutorialStepShown() {
+ mStatsLogManager.logger().log(
+ StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_HOME_STEP_SHOWN);
+ }
+
+ @Override
+ void logTutorialStepCompleted() {
+ mStatsLogManager.logger().log(
+ StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_HOME_STEP_COMPLETED);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
index f63a945..c7e24db 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
@@ -18,12 +18,14 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
+import android.content.SharedPreferences;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
import com.android.launcher3.R;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.quickstep.interaction.TutorialController.TutorialType;
import java.util.ArrayList;
@@ -31,6 +33,11 @@
/** Shows the Overview gesture interactive tutorial. */
public class OverviewGestureTutorialFragment extends TutorialFragment {
+ protected OverviewGestureTutorialFragment(
+ SharedPreferences sharedPrefs, StatsLogManager statsLogManager) {
+ super(sharedPrefs, statsLogManager);
+ }
+
@Nullable
@Override
Integer getEdgeAnimationResId() {
@@ -111,4 +118,16 @@
releaseFeedbackAnimation();
return super.onTouch(view, motionEvent);
}
+
+ @Override
+ void logTutorialStepShown() {
+ mStatsLogManager.logger().log(
+ StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_OVERVIEW_STEP_SHOWN);
+ }
+
+ @Override
+ void logTutorialStepCompleted() {
+ mStatsLogManager.logger().log(
+ StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_OVERVIEW_STEP_COMPLETED);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialFragment.java
index 955a2f7..92a2731 100644
--- a/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialFragment.java
@@ -15,14 +15,21 @@
*/
package com.android.quickstep.interaction;
+import android.content.SharedPreferences;
import android.view.MotionEvent;
import android.view.View;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.quickstep.interaction.TutorialController.TutorialType;
/** Shows the general navigation gesture sandbox environment. */
public class SandboxModeTutorialFragment extends TutorialFragment {
+ protected SandboxModeTutorialFragment(
+ SharedPreferences sharedPrefs, StatsLogManager statsLogManager) {
+ super(sharedPrefs, statsLogManager);
+ }
+
@Override
TutorialController createController(TutorialType type) {
return new SandboxModeTutorialController(this, type);
@@ -40,4 +47,14 @@
}
return super.onTouch(view, motionEvent);
}
+
+ @Override
+ void logTutorialStepShown() {
+ // No-Op: tutorial step not currently shown to users
+ }
+
+ @Override
+ void logTutorialStepCompleted() {
+ // No-Op: tutorial step not currently shown to users
+ }
}
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 3c88988..6a8894e 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -81,7 +81,8 @@
TutorialType mTutorialType;
final Context mContext;
- final TextView mCloseButton;
+ final TextView mSkipButton;
+ final Button mDoneButton;
final ViewGroup mFeedbackView;
final TextView mFeedbackTitleView;
final ImageView mEdgeGestureVideoView;
@@ -94,7 +95,6 @@
final AnimatedTaskView mFakePreviousTaskView;
final View mRippleView;
final RippleDrawable mRippleDrawable;
- final Button mActionButton;
final TutorialStepIndicator mTutorialStepView;
final ImageView mFingerDotView;
private final AlertDialog mSkipTutorialDialog;
@@ -115,8 +115,8 @@
mContext = mTutorialFragment.getContext();
RootSandboxLayout rootView = tutorialFragment.getRootView();
- mCloseButton = rootView.findViewById(R.id.gesture_tutorial_fragment_close_button);
- mCloseButton.setOnClickListener(button -> showSkipTutorialDialog());
+ mSkipButton = rootView.findViewById(R.id.gesture_tutorial_fragment_close_button);
+ mSkipButton.setOnClickListener(button -> showSkipTutorialDialog());
mFeedbackView = rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_view);
mFeedbackTitleView = mFeedbackView.findViewById(
R.id.gesture_tutorial_fragment_feedback_title);
@@ -130,7 +130,7 @@
rootView.findViewById(R.id.gesture_tutorial_fake_previous_task_view);
mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view);
mRippleDrawable = (RippleDrawable) mRippleView.getBackground();
- mActionButton = rootView.findViewById(R.id.gesture_tutorial_fragment_action_button);
+ mDoneButton = rootView.findViewById(R.id.gesture_tutorial_fragment_action_button);
mTutorialStepView =
rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_tutorial_step);
mFingerDotView = rootView.findViewById(R.id.gesture_tutorial_finger_dot);
@@ -431,22 +431,22 @@
}
void updateCloseButton() {
- mCloseButton.setTextAppearance(Utilities.isDarkTheme(mContext)
+ mSkipButton.setTextAppearance(Utilities.isDarkTheme(mContext)
? R.style.TextAppearance_GestureTutorial_Feedback_Subtext
: R.style.TextAppearance_GestureTutorial_Feedback_Subtext_Dark);
}
void hideActionButton() {
- mCloseButton.setVisibility(View.VISIBLE);
+ mSkipButton.setVisibility(View.VISIBLE);
// Invisible to maintain the layout.
- mActionButton.setVisibility(View.INVISIBLE);
- mActionButton.setOnClickListener(null);
+ mDoneButton.setVisibility(View.INVISIBLE);
+ mDoneButton.setOnClickListener(null);
}
void showActionButton() {
- mCloseButton.setVisibility(GONE);
- mActionButton.setVisibility(View.VISIBLE);
- mActionButton.setOnClickListener(this::onActionButtonClicked);
+ mSkipButton.setVisibility(GONE);
+ mDoneButton.setVisibility(View.VISIBLE);
+ mDoneButton.setOnClickListener(this::onActionButtonClicked);
}
void hideFakeTaskbar(boolean animateToHotseat) {
@@ -609,7 +609,7 @@
R.id.gesture_tutorial_dialog_confirm_button);
if (confirmButton != null) {
confirmButton.setOnClickListener(v -> {
- sandboxActivity.closeTutorial();
+ mTutorialFragment.closeTutorial(true);
tutorialDialog.dismiss();
});
} else {
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index 4b836e3..d79b946 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -20,11 +20,13 @@
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.graphics.Insets;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.util.ArraySet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -43,14 +45,24 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.quickstep.interaction.TutorialController.TutorialType;
+import java.util.Set;
+
abstract class TutorialFragment extends Fragment implements OnTouchListener {
private static final String LOG_TAG = "TutorialFragment";
static final String KEY_TUTORIAL_TYPE = "tutorial_type";
static final String KEY_GESTURE_COMPLETE = "gesture_complete";
+ private static final String TUTORIAL_SKIPPED_PREFERENCE_KEY = "pref_gestureTutorialSkipped";
+ private static final String COMPLETED_TUTORIAL_STEPS_PREFERENCE_KEY =
+ "pref_completedTutorialSteps";
+
+ private final SharedPreferences mSharedPrefs;
+ protected final StatsLogManager mStatsLogManager;
+
TutorialType mTutorialType;
boolean mGestureComplete = false;
@Nullable TutorialController mTutorialController = null;
@@ -71,10 +83,15 @@
private boolean mIsLargeScreen;
private boolean mIsFoldable;
- public static TutorialFragment newInstance(TutorialType tutorialType, boolean gestureComplete) {
- TutorialFragment fragment = getFragmentForTutorialType(tutorialType);
+ public static TutorialFragment newInstance(
+ TutorialType tutorialType,
+ boolean gestureComplete,
+ SharedPreferences sharedPrefs,
+ StatsLogManager statsLogManager) {
+ TutorialFragment fragment =
+ getFragmentForTutorialType(tutorialType, sharedPrefs, statsLogManager);
if (fragment == null) {
- fragment = new BackGestureTutorialFragment();
+ fragment = new BackGestureTutorialFragment(sharedPrefs, statsLogManager);
tutorialType = TutorialType.BACK_NAVIGATION;
}
@@ -86,28 +103,36 @@
}
@Nullable
- private static TutorialFragment getFragmentForTutorialType(TutorialType tutorialType) {
+ private static TutorialFragment getFragmentForTutorialType(
+ TutorialType tutorialType,
+ SharedPreferences sharedPrefs,
+ StatsLogManager statsLogManager) {
switch (tutorialType) {
case BACK_NAVIGATION:
case BACK_NAVIGATION_COMPLETE:
- return new BackGestureTutorialFragment();
+ return new BackGestureTutorialFragment(sharedPrefs, statsLogManager);
case HOME_NAVIGATION:
case HOME_NAVIGATION_COMPLETE:
- return new HomeGestureTutorialFragment();
+ return new HomeGestureTutorialFragment(sharedPrefs, statsLogManager);
case OVERVIEW_NAVIGATION:
case OVERVIEW_NAVIGATION_COMPLETE:
- return new OverviewGestureTutorialFragment();
+ return new OverviewGestureTutorialFragment(sharedPrefs, statsLogManager);
case ASSISTANT:
case ASSISTANT_COMPLETE:
- return new AssistantGestureTutorialFragment();
+ return new AssistantGestureTutorialFragment(sharedPrefs, statsLogManager);
case SANDBOX_MODE:
- return new SandboxModeTutorialFragment();
+ return new SandboxModeTutorialFragment(sharedPrefs, statsLogManager);
default:
Log.e(LOG_TAG, "Failed to find an appropriate fragment for " + tutorialType.name());
}
return null;
}
+ protected TutorialFragment(SharedPreferences sharedPrefs, StatsLogManager statsLogManager) {
+ mSharedPrefs = sharedPrefs;
+ mStatsLogManager = statsLogManager;
+ }
+
@Nullable Integer getEdgeAnimationResId() {
return null;
}
@@ -315,6 +340,7 @@
}
void onAttachedToWindow() {
+ logTutorialStepShown();
mEdgeBackGestureHandler.setViewGroupParent(getRootView());
}
@@ -348,8 +374,16 @@
}
void continueTutorial() {
- GestureSandboxActivity gestureSandboxActivity = getGestureSandboxActivity();
+ Set<String> updatedCompletedSteps = new ArraySet<>(mSharedPrefs.getStringSet(
+ COMPLETED_TUTORIAL_STEPS_PREFERENCE_KEY, new ArraySet<>()));
+ updatedCompletedSteps.add(mTutorialType.toString());
+
+ mSharedPrefs.edit().putStringSet(
+ COMPLETED_TUTORIAL_STEPS_PREFERENCE_KEY, updatedCompletedSteps).apply();
+ logTutorialStepCompleted();
+
+ GestureSandboxActivity gestureSandboxActivity = getGestureSandboxActivity();
if (gestureSandboxActivity == null) {
closeTutorial();
return;
@@ -358,6 +392,15 @@
}
void closeTutorial() {
+ closeTutorial(false);
+ }
+
+ void closeTutorial(boolean tutorialSkipped) {
+ if (tutorialSkipped) {
+ mSharedPrefs.edit().putBoolean(TUTORIAL_SKIPPED_PREFERENCE_KEY, true).apply();
+ mStatsLogManager.logger().log(
+ StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_SKIPPED);
+ }
FragmentActivity activity = getActivity();
if (activity != null) {
activity.setResult(Activity.RESULT_OK);
@@ -390,6 +433,10 @@
|| (mTutorialController != null && mTutorialController.isGestureCompleted());
}
+ abstract void logTutorialStepShown();
+
+ abstract void logTutorialStepCompleted();
+
@Nullable
private GestureSandboxActivity getGestureSandboxActivity() {
Context context = getContext();
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 0fa2b24..6b15807 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -216,14 +216,12 @@
// Align vertically, using taskbar height + mDp.taskbarOffsetY() to estimate where
// the button nav top is.
- View startActionView = findViewById(R.id.action_screenshot);
int marginBottom = getOverviewActionsBottomMarginPx(
DisplayController.getNavigationMode(getContext()), mDp);
int actionsTop =
- (mDp.heightPx - marginBottom - mInsets.bottom) - startActionView.getHeight();
+ (mDp.heightPx - marginBottom - mInsets.bottom) - mDp.overviewActionsHeight;
int navTop = mDp.heightPx - (mDp.taskbarSize + mDp.getTaskbarOffsetY());
- int transY = navTop - actionsTop
- + ((mDp.taskbarSize - startActionView.getHeight()) / 2);
+ int transY = navTop - actionsTop + ((mDp.taskbarSize - mDp.overviewActionsHeight) / 2);
setTranslationY(transY);
} else {
setPadding(mInsets.left, 0, mInsets.right, 0);
diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
index 3e84a76..3bd3722 100644
--- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
+++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
@@ -48,7 +48,7 @@
Duration.ofSeconds(600), Duration.ofSeconds(300),
PendingIntent.getActivity(mTargetContext, -1, new Intent(), 0)));
- mLauncher.pressHome();
+ mLauncher.goHome();
final DigitalWellBeingToast toast = getToast();
waitForLauncherCondition("Toast is not visible", launcher -> toast.hasLimit());
@@ -58,7 +58,7 @@
runWithShellPermission(
() -> usageStatsManager.unregisterAppUsageLimitObserver(observerId));
- mLauncher.pressHome();
+ mLauncher.goHome();
assertFalse("Toast is visible", getToast().hasLimit());
} finally {
runWithShellPermission(
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index 5351963..6ec6269 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -41,7 +41,7 @@
super.setUp();
TaplTestsLauncher3.initialize(this);
// b/143488140
- mLauncher.pressHome();
+ mLauncher.goHome();
// Start an activity where the gestures start.
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
}
@@ -54,7 +54,7 @@
// The test action.
eventProcessor.startIteration();
- mLauncher.pressHome();
+ mLauncher.goHome();
eventProcessor.finishIteration();
}
@@ -66,7 +66,7 @@
closeLauncherActivity();
// The test action.
- mLauncher.pressHome();
+ mLauncher.goHome();
}
}
@@ -81,6 +81,6 @@
mLauncher.getLaunchedAppState().switchToOverview();
}
closeLauncherActivity();
- mLauncher.pressHome();
+ mLauncher.goHome();
}
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 396cb1b..399cd10 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -102,7 +102,7 @@
public void testOverview() throws Exception {
startTestAppsWithCheck();
// mLauncher.pressHome() also tests an important case of pressing home while in background.
- Overview overview = mLauncher.pressHome().switchToOverview();
+ Overview overview = mLauncher.goHome().switchToOverview();
assertTrue("Launcher internal state didn't switch to Overview",
isInState(() -> LauncherState.OVERVIEW));
executeOnLauncher(
@@ -127,7 +127,7 @@
getCurrentOverviewPage(launcher) < currentTaskAfterFlingForward));
// Test opening a task.
- OverviewTask task = mLauncher.pressHome().switchToOverview().getCurrentTask();
+ OverviewTask task = mLauncher.goHome().switchToOverview().getCurrentTask();
assertNotNull("overview.getCurrentTask() returned null (1)", task);
assertNotNull("OverviewTask.open returned null", task.open());
assertTrue("Test activity didn't open from Overview", mDevice.wait(Until.hasObject(
@@ -139,7 +139,7 @@
isInLaunchedApp(launcher)));
// Test dismissing a task.
- overview = mLauncher.pressHome().switchToOverview();
+ overview = mLauncher.goHome().switchToOverview();
assertTrue("Launcher internal state didn't switch to Overview",
isInState(() -> LauncherState.OVERVIEW));
final Integer numTasks = getFromLauncher(launcher -> getTaskCount(launcher));
@@ -151,7 +151,7 @@
numTasks - 1, getTaskCount(launcher)));
// Test dismissing all tasks.
- mLauncher.pressHome().switchToOverview().dismissAllTasks();
+ mLauncher.goHome().switchToOverview().dismissAllTasks();
assertTrue("Launcher internal state is not Home",
isInState(() -> LauncherState.NORMAL));
executeOnLauncher(
@@ -168,14 +168,14 @@
@ScreenRecord // b/195673272
public void testOverviewActions() throws Exception {
// Experimenting for b/165029151:
- final Overview overview = mLauncher.pressHome().switchToOverview();
+ final Overview overview = mLauncher.goHome().switchToOverview();
if (overview.hasTasks()) overview.dismissAllTasks();
- mLauncher.pressHome();
+ mLauncher.goHome();
//
startTestAppsWithCheck();
OverviewActions actionsView =
- mLauncher.pressHome().switchToOverview().getOverviewActions();
+ mLauncher.goHome().switchToOverview().getOverviewActions();
actionsView.clickAndDismissScreenshot();
}
@@ -200,7 +200,7 @@
@PortraitLandscape
public void testSwitchToOverview() throws Exception {
assertNotNull("Workspace.switchToOverview() returned null",
- mLauncher.pressHome().switchToOverview());
+ mLauncher.goHome().switchToOverview());
assertTrue("Launcher internal state didn't switch to Overview",
isInState(() -> LauncherState.OVERVIEW));
}
@@ -240,7 +240,7 @@
// Testing pressHome.
assertTrue("Launcher internal state is not All Apps",
isInState(() -> LauncherState.ALL_APPS));
- assertNotNull("pressHome returned null", mLauncher.pressHome());
+ assertNotNull("pressHome returned null", mLauncher.goHome());
assertTrue("Launcher internal state is not Home",
isInState(() -> LauncherState.NORMAL));
assertNotNull("getHome returned null", mLauncher.getWorkspace());
@@ -289,7 +289,7 @@
@PortraitLandscape
public void testQuickSwitchFromHome() throws Exception {
startTestActivity(2);
- mLauncher.pressHome().quickSwitchToPreviousApp();
+ mLauncher.goHome().quickSwitchToPreviousApp();
assertTrue("The most recent task is not running after quick switching from home",
isTestActivityRunning(2));
getAndAssertLaunchedApp();
@@ -329,7 +329,7 @@
startTestActivity(i);
}
- Overview overview = mLauncher.pressHome().switchToOverview();
+ Overview overview = mLauncher.goHome().switchToOverview();
executeOnLauncher(
launcher -> assertTrue("Don't have at least 13 tasks",
getTaskCount(launcher) >= 13));
@@ -348,7 +348,7 @@
DEFAULT_UI_TIMEOUT));
// Scroll the task offscreen as it is now first
- overview = mLauncher.pressHome().switchToOverview();
+ overview = mLauncher.goHome().switchToOverview();
overview.scrollCurrentTaskOffScreen();
assertTrue("Launcher internal state is not Overview",
isInState(() -> LauncherState.OVERVIEW));
@@ -377,7 +377,7 @@
launcher)) <= 1)));
// Test dismissing all tasks.
- mLauncher.pressHome().switchToOverview().dismissAllTasks();
+ mLauncher.goHome().switchToOverview().dismissAllTasks();
assertTrue("Launcher internal state is not Home",
isInState(() -> LauncherState.NORMAL));
executeOnLauncher(
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
new file mode 100644
index 0000000..ba93975
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2022 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 static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static junit.framework.TestCase.assertEquals;
+
+import android.content.Intent;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.tapl.Taskbar;
+import com.android.launcher3.ui.TaplTestsLauncher3;
+
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TaplTestsTaskbar extends AbstractQuickStepTest {
+
+ private static final String TEST_APP_NAME = "LauncherTestApp";
+ private static final String TEST_APP_PACKAGE =
+ getInstrumentation().getContext().getPackageName();
+ private static final String CALCULATOR_APP_PACKAGE =
+ resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR);
+
+ @Override
+ public void setUp() throws Exception {
+ Assume.assumeTrue(mLauncher.isTablet());
+ super.setUp();
+ mLauncher.useTestWorkspaceLayoutOnReload();
+ TaplTestsLauncher3.initialize(this);
+
+ startAppFast(CALCULATOR_APP_PACKAGE);
+ mLauncher.showTaskbarIfHidden();
+ }
+
+ @After
+ public void tearDown() {
+ mLauncher.useDefaultWorkspaceLayoutOnReload();
+ }
+
+ @Test
+ public void testHideShowTaskbar() {
+ getTaskbar().hide();
+ mLauncher.getLaunchedAppState().showTaskbar();
+ }
+
+ @Test
+ public void testLaunchApp() throws Exception {
+ getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
+ }
+
+ @Test
+ public void testOpenMenu() throws Exception {
+ getTaskbar().getAppIcon(TEST_APP_NAME).openMenu();
+ }
+
+ @Test
+ public void testLaunchShortcut() throws Exception {
+ getTaskbar().getAppIcon(TEST_APP_NAME)
+ .openDeepShortcutMenu()
+ .getMenuItem("Shortcut 1")
+ .launch(TEST_APP_PACKAGE);
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testLaunchAppInSplitscreen() throws Exception {
+ getTaskbar().getAppIcon(TEST_APP_NAME).dragToSplitscreen(
+ TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testLaunchShortcutInSplitscreen() throws Exception {
+ getTaskbar().getAppIcon(TEST_APP_NAME)
+ .openDeepShortcutMenu()
+ .getMenuItem("Shortcut 1")
+ .dragToSplitscreen(TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
+ }
+
+ @Test
+ public void testLaunchApp_FromTaskbarAllApps() throws Exception {
+ getTaskbar().openAllApps().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
+ }
+
+ @Test
+ public void testOpenMenu_FromTaskbarAllApps() throws Exception {
+ getTaskbar().openAllApps().getAppIcon(TEST_APP_NAME).openMenu();
+ }
+
+ @Test
+ public void testLaunchShortcut_FromTaskbarAllApps() throws Exception {
+ getTaskbar().openAllApps()
+ .getAppIcon(TEST_APP_NAME)
+ .openDeepShortcutMenu()
+ .getMenuItem("Shortcut 1")
+ .launch(TEST_APP_PACKAGE);
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testLaunchAppInSplitscreen_FromTaskbarAllApps() throws Exception {
+ getTaskbar().openAllApps()
+ .getAppIcon(TEST_APP_NAME)
+ .dragToSplitscreen(TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testLaunchShortcutInSplitscreen_FromTaskbarAllApps() throws Exception {
+ getTaskbar().openAllApps()
+ .getAppIcon(TEST_APP_NAME)
+ .openDeepShortcutMenu()
+ .getMenuItem("Shortcut 1")
+ .dragToSplitscreen(TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
+ }
+
+ private Taskbar getTaskbar() {
+ Taskbar taskbar = mLauncher.getLaunchedAppState().getTaskbar();
+ List<String> taskbarIconNames = taskbar.getIconNames();
+ List<String> hotseatIconNames = mLauncher.getHotseatIconNames();
+
+ assertEquals("Taskbar and hotseat icon counts do not match",
+ taskbarIconNames.size(), hotseatIconNames.size());
+
+ for (int i = 0; i < taskbarIconNames.size(); i++) {
+ assertEquals("Taskbar and Hotseat icons do not match",
+ taskbarIconNames, hotseatIconNames);
+ }
+
+ return taskbar;
+ }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
index 661beda..7e408a8 100644
--- a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
+++ b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
@@ -197,7 +197,7 @@
addItemToScreen(item);
assertTrue("Widget is not present",
- mLauncher.pressHome().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
+ mLauncher.goHome().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
int widgetId = item.appWidgetId;
// Verify widget id
@@ -221,7 +221,7 @@
// Widget is updated when going home
mInitTracker.disableLog();
- mLauncher.pressHome();
+ mLauncher.goHome();
verifyWidget(finalWidgetText);
assertNotEquals(1, mInitTracker.viewInitCount);
} finally {
diff --git a/res/values-sw720dp-land/dimens.xml b/res/values-sw720dp-land/dimens.xml
index a9e0fb8..9821526 100644
--- a/res/values-sw720dp-land/dimens.xml
+++ b/res/values-sw720dp-land/dimens.xml
@@ -15,6 +15,9 @@
-->
<resources>
-<!-- AllApps -->
- <dimen name="all_apps_top_padding">0dp</dimen>
+ <!-- Widget picker-->
+ <dimen name="widget_list_horizontal_margin">49dp</dimen>
+
+ <!-- Bottom sheet-->
+ <dimen name="bottom_sheet_extra_top_padding">0dp</dimen>
</resources>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index 5c314d5..7ebc3f8 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -16,6 +16,11 @@
<resources>
<!-- AllApps -->
- <dimen name="all_apps_top_padding">300dp</dimen>
<dimen name="all_apps_bottom_sheet_horizontal_padding">65dp</dimen>
+
+<!-- Widget picker-->
+ <dimen name="widget_list_horizontal_margin">30dp</dimen>
+
+<!-- Bottom sheet-->
+ <dimen name="bottom_sheet_extra_top_padding">300dp</dimen>
</resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 509f363..e2fd0e3 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -166,4 +166,8 @@
<!-- Name of the class used to generate colors from the wallpaper colors. Must be implementing the LauncherAppWidgetHostView.ColorGenerator interface. -->
<string name="color_generator_class" translatable="false"/>
+ <!-- Swipe back to home related -->
+ <dimen name="swipe_back_window_scale_x_margin">10dp</dimen>
+ <dimen name="swipe_back_window_scale_y_margin">80dp</dimen>
+ <dimen name="swipe_back_window_corner_radius">40dp</dimen>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 3e666fc..338e218 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -94,7 +94,6 @@
<!-- All Apps -->
<dimen name="all_apps_starting_vertical_translate">320dp</dimen>
- <dimen name="all_apps_top_padding">0dp</dimen>
<dimen name="all_apps_search_bar_field_height">48dp</dimen>
<!-- all_apps_search_bar_field_height / 2 -->
<dimen name="all_apps_search_bar_content_overlap">24dp</dimen>
@@ -345,6 +344,7 @@
<dimen name="task_thumbnail_icon_drawable_size_grid">0dp</dimen>
<dimen name="overview_task_margin">0dp</dimen>
<dimen name="overview_task_margin_grid">0dp</dimen>
+ <dimen name="overview_actions_height">0dp</dimen>
<dimen name="overview_actions_button_spacing">0dp</dimen>
<dimen name="overview_actions_margin_gesture">0dp</dimen>
<dimen name="overview_actions_top_margin_gesture">0dp</dimen>
@@ -367,6 +367,7 @@
<dimen name="padded_rounded_button_padding">8dp</dimen>
<!-- Bottom sheet related parameters -->
+ <dimen name="bottom_sheet_extra_top_padding">0dp</dimen>
<dimen name="bottom_sheet_handle_width">32dp</dimen>
<dimen name="bottom_sheet_handle_height">4dp</dimen>
<dimen name="bottom_sheet_handle_margin">16dp</dimen>
diff --git a/res/xml/default_test_workspace.xml b/res/xml/default_test_workspace.xml
new file mode 100644
index 0000000..bd718b3
--- /dev/null
+++ b/res/xml/default_test_workspace.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+
+<!-- Split display specific version of Launcher3/res/xml/default_workspace_4x4.xml -->
+<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
+
+ <!-- Launcher Test Activity -->
+ <resolve
+ launcher:container="-101"
+ launcher:screen="0"
+ launcher:x="0"
+ launcher:y="0" >
+ <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.CATEGORY_TEST;component=com.google.android.apps.nexuslauncher.tests/com.android.launcher3.testcomponent.BaseTestingActivity;end" />
+ </resolve>
+
+</favorites>
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 041bee9..3bc0f6d 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -867,6 +867,11 @@
}
protected void applyCompoundDrawables(Drawable icon) {
+ if (icon == null) {
+ // Icon can be null when we use the BubbleTextView for text only.
+ return;
+ }
+
// If we had already set an icon before, disable relayout as the icon size is the
// same as before.
mDisableRelayout = mIcon != null;
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 1b333d8..622d750 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -175,6 +175,7 @@
public Point allAppsBorderSpacePx;
public int allAppsShiftRange;
public int allAppsTopPadding;
+ public int bottomSheetTopPadding;
public int allAppsCellHeightPx;
public int allAppsCellWidthPx;
public int allAppsIconSizePx;
@@ -191,6 +192,7 @@
public int overviewTaskIconDrawableSizePx;
public int overviewTaskIconDrawableSizeGridPx;
public int overviewTaskThumbnailTopMarginPx;
+ public final int overviewActionsHeight;
public final int overviewActionsMarginThreeButtonPx;
public final int overviewActionsTopMarginGesturePx;
public final int overviewActionsBottomMarginGesturePx;
@@ -293,8 +295,11 @@
desiredWorkspaceHorizontalMarginPx = getHorizontalMarginPx(inv, res);
desiredWorkspaceHorizontalMarginOriginalPx = desiredWorkspaceHorizontalMarginPx;
- allAppsTopPadding = res.getDimensionPixelSize(R.dimen.all_apps_top_padding)
- + (isTablet ? heightPx - availableHeightPx : 0);
+ bottomSheetTopPadding = windowBounds.insets.top // statusbar height
+ + res.getDimensionPixelSize(R.dimen.bottom_sheet_extra_top_padding)
+ + (isTablet ? 0 : edgeMarginPx); // phones need edgeMarginPx additional padding
+
+ allAppsTopPadding = isTablet ? bottomSheetTopPadding : 0;
allAppsShiftRange = isTablet
? heightPx - allAppsTopPadding
: res.getDimensionPixelSize(R.dimen.all_apps_starting_vertical_translate);
@@ -407,6 +412,7 @@
overviewPageSpacing = res.getDimensionPixelSize(R.dimen.overview_page_spacing);
overviewActionsButtonSpacing = res.getDimensionPixelSize(
R.dimen.overview_actions_button_spacing);
+ overviewActionsHeight = res.getDimensionPixelSize(R.dimen.overview_actions_height);
overviewActionsMarginThreeButtonPx = res.getDimensionPixelSize(
R.dimen.overview_actions_margin_three_button);
// Grid task's top margin is only overviewTaskIconSizePx + overviewTaskMarginGridPx, but
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index e004a4b..5aa8a46 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -102,6 +102,8 @@
public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".settings";
public static final String KEY_LAYOUT_PROVIDER_AUTHORITY = "KEY_LAYOUT_PROVIDER_AUTHORITY";
+ private static final int TEST_WORKSPACE_LAYOUT_RES_XML = R.xml.default_test_workspace;
+
static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
protected DatabaseHelper mOpenHelper;
@@ -109,6 +111,8 @@
private long mLastRestoreTimestamp = 0L;
+ private boolean mUseTestWorkspaceLayout;
+
/**
* $ adb shell dumpsys activity provider com.android.launcher3
*/
@@ -158,6 +162,7 @@
private synchronized boolean prepForMigration(String dbFile, String targetTableName,
Supplier<DatabaseHelper> src, Supplier<DatabaseHelper> dst) {
if (TextUtils.equals(dbFile, mOpenHelper.getDatabaseName())) {
+ Log.e("b/198965093", "prepForMigration - target db is same as current: " + dbFile);
return false;
}
@@ -390,6 +395,14 @@
mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
return null;
}
+ case LauncherSettings.Settings.METHOD_SET_USE_TEST_WORKSPACE_LAYOUT_FLAG: {
+ mUseTestWorkspaceLayout = true;
+ return null;
+ }
+ case LauncherSettings.Settings.METHOD_CLEAR_USE_TEST_WORKSPACE_LAYOUT_FLAG: {
+ mUseTestWorkspaceLayout = false;
+ return null;
+ }
case LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES: {
loadDefaultFavoritesIfNecessary();
return null;
@@ -427,7 +440,7 @@
Bundle result = new Bundle();
result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
prepForMigration(
- InvariantDeviceProfile.INSTANCE.get(getContext()).dbFile,
+ arg /* dbFile */,
Favorites.TMP_TABLE,
() -> mOpenHelper,
() -> DatabaseHelper.createDatabaseHelper(
@@ -609,7 +622,8 @@
private DefaultLayoutParser getDefaultLayoutParser(AppWidgetHost widgetHost) {
InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
- int defaultLayout = idp.defaultLayoutId;
+ int defaultLayout = mUseTestWorkspaceLayout
+ ? TEST_WORKSPACE_LAYOUT_RES_XML : idp.defaultLayoutId;
if (getContext().getSystemService(UserManager.class).isDemoUser()
&& idp.demoModeLayoutId != 0) {
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 048aaaa..66195f3 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -374,6 +374,12 @@
public static final String METHOD_CREATE_EMPTY_DB = "create_empty_db";
+ public static final String METHOD_SET_USE_TEST_WORKSPACE_LAYOUT_FLAG =
+ "set_use_test_workspace_layout_flag";
+
+ public static final String METHOD_CLEAR_USE_TEST_WORKSPACE_LAYOUT_FLAG =
+ "clear_use_test_workspace_layout_flag";
+
public static final String METHOD_LOAD_DEFAULT_FAVORITES = "load_default_favorites";
public static final String METHOD_REMOVE_GHOST_WIDGETS = "remove_ghost_widgets";
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index f542d8e..f913aa9 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -442,8 +442,8 @@
if (showTabs == mUsingTabs && !force) {
return;
}
- replaceRVContainer(showTabs);
mUsingTabs = showTabs;
+ replaceRVContainer(mUsingTabs);
mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.MAIN).mRecyclerView);
mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView);
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index 09344c8..fd8945a 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -100,14 +100,12 @@
@Override
public void afterTextChanged(final Editable s) {
mQuery = s.toString();
- mInput.getSelectionStart();
if (mQuery.isEmpty()) {
mSearchAlgorithm.cancel(true);
mCallback.clearSearchResult();
} else {
mSearchAlgorithm.cancel(false);
- mSearchAlgorithm.doSearch(mQuery, mInput.getSelectionStart(), mTextConversions,
- mCallback);
+ mSearchAlgorithm.doSearch(mQuery, mTextConversions, mCallback);
}
}
@@ -117,7 +115,7 @@
}
// If play store continues auto updating an app, we want to show partial result.
mSearchAlgorithm.cancel(false);
- mSearchAlgorithm.doSearch(mQuery, mInput.getSelectionStart(), mCallback);
+ mSearchAlgorithm.doSearch(mQuery, mCallback);
}
@Override
diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
index 62b7807..222c8fe 100644
--- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
@@ -58,7 +58,7 @@
}
@Override
- public void doSearch(String query, int cursorLocation, SearchCallback<AdapterItem> callback) {
+ public void doSearch(String query, SearchCallback<AdapterItem> callback) {
mAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 278bcf2..df42355 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -52,7 +52,7 @@
* Enable moving the QSB on the 0th screen of the workspace. This is not a configuration feature
* and should be modified at a project level.
*/
- public static final boolean QSB_ON_FIRST_SCREEN = true;
+ public static final boolean QSB_ON_FIRST_SCREEN = BuildConfig.QSB_ON_FIRST_SCREEN;
/**
* Feature flag to handle define config changes dynamically instead of killing the process.
@@ -89,9 +89,6 @@
public static final BooleanFlag ENABLE_DEVICE_SEARCH = new DeviceFlag(
"ENABLE_DEVICE_SEARCH", true, "Allows on device search in all apps");
- public static final BooleanFlag ENABLE_ONE_SEARCH = new DeviceFlag("ENABLE_ONE_SEARCH", false,
- "Use homescreen search box to complete allApps searches");
-
public static final BooleanFlag ENABLE_FLOATING_SEARCH_BAR =
getDebugFlag("ENABLE_FLOATING_SEARCH_BAR", false,
"Keep All Apps search bar at the bottom (but above keyboard if open)");
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index b914ae2..a3945fd 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -98,6 +98,7 @@
final ValueAnimator mAnim;
// Whether mAnim has started. Unlike mAnim.isStarted(), this is true even after mAnim ends.
private boolean mAnimStarted;
+ private Runnable mOnAnimEndCallback = null;
private int mLastTouchX;
private int mLastTouchY;
@@ -180,6 +181,14 @@
public void onAnimationStart(Animator animation) {
mAnimStarted = true;
}
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ if (mOnAnimEndCallback != null) {
+ mOnAnimEndCallback.run();
+ }
+ }
});
setDragRegion(new Rect(0, 0, width, height));
@@ -199,6 +208,10 @@
setWillNotDraw(false);
}
+ public void setOnAnimationEndCallback(Runnable callback) {
+ mOnAnimEndCallback = callback;
+ }
+
/**
* Initialize {@code #mIconDrawable} if the item can be represented using
* an {@link AdaptiveIconDrawable} or {@link FolderAdaptiveIcon}.
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index f392e95..e6dc21e 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -529,6 +529,27 @@
@UiEvent(doc = "User clicks on the search icon on header to launch search in app.")
LAUNCHER_ALLAPPS_SEARCHINAPP_LAUNCH(913),
+ @UiEvent(doc = "User is shown the back gesture navigation tutorial step.")
+ LAUNCHER_GESTURE_TUTORIAL_BACK_STEP_SHOWN(959),
+
+ @UiEvent(doc = "User is shown the home gesture navigation tutorial step.")
+ LAUNCHER_GESTURE_TUTORIAL_HOME_STEP_SHOWN(960),
+
+ @UiEvent(doc = "User is shown the overview gesture navigation tutorial step.")
+ LAUNCHER_GESTURE_TUTORIAL_OVERVIEW_STEP_SHOWN(961),
+
+ @UiEvent(doc = "User completed the back gesture navigation tutorial step.")
+ LAUNCHER_GESTURE_TUTORIAL_BACK_STEP_COMPLETED(962),
+
+ @UiEvent(doc = "User completed the home gesture navigation tutorial step.")
+ LAUNCHER_GESTURE_TUTORIAL_HOME_STEP_COMPLETED(963),
+
+ @UiEvent(doc = "User completed the overview gesture navigation tutorial step.")
+ LAUNCHER_GESTURE_TUTORIAL_OVERVIEW_STEP_COMPLETED(964),
+
+ @UiEvent(doc = "User skips the gesture navigation tutorial.")
+ LAUNCHER_GESTURE_TUTORIAL_SKIPPED(965),
+
@UiEvent(doc = "User scrolled on one of the all apps surfaces such as A-Z list, search "
+ "result page etc.")
LAUNCHER_ALLAPPS_SCROLLED(985);
diff --git a/src/com/android/launcher3/model/DeviceGridState.java b/src/com/android/launcher3/model/DeviceGridState.java
index 3e49d79..35fcb78 100644
--- a/src/com/android/launcher3/model/DeviceGridState.java
+++ b/src/com/android/launcher3/model/DeviceGridState.java
@@ -43,15 +43,18 @@
public static final String KEY_WORKSPACE_SIZE = "migration_src_workspace_size";
public static final String KEY_HOTSEAT_COUNT = "migration_src_hotseat_count";
public static final String KEY_DEVICE_TYPE = "migration_src_device_type";
+ public static final String KEY_DB_FILE = "migration_src_db_file";
private final String mGridSizeString;
private final int mNumHotseat;
private final @DeviceType int mDeviceType;
+ private final String mDbFile;
public DeviceGridState(InvariantDeviceProfile idp) {
mGridSizeString = String.format(Locale.ENGLISH, "%d,%d", idp.numColumns, idp.numRows);
mNumHotseat = idp.numDatabaseHotseatIcons;
mDeviceType = idp.deviceType;
+ mDbFile = idp.dbFile;
}
public DeviceGridState(Context context) {
@@ -59,6 +62,7 @@
mGridSizeString = prefs.getString(KEY_WORKSPACE_SIZE, "");
mNumHotseat = prefs.getInt(KEY_HOTSEAT_COUNT, -1);
mDeviceType = prefs.getInt(KEY_DEVICE_TYPE, TYPE_PHONE);
+ mDbFile = prefs.getString(KEY_DB_FILE, "");
}
/**
@@ -69,6 +73,20 @@
}
/**
+ * Returns the databaseFile for the grid.
+ */
+ public String getDbFile() {
+ return mDbFile;
+ }
+
+ /**
+ * Returns the number of hotseat icons.
+ */
+ public int getNumHotseat() {
+ return mNumHotseat;
+ }
+
+ /**
* Stores the device state to shared preferences
*/
public void writeToPrefs(Context context) {
@@ -76,6 +94,7 @@
.putString(KEY_WORKSPACE_SIZE, mGridSizeString)
.putInt(KEY_HOTSEAT_COUNT, mNumHotseat)
.putInt(KEY_DEVICE_TYPE, mDeviceType)
+ .putString(KEY_DB_FILE, mDbFile)
.apply();
}
@@ -106,6 +125,7 @@
+ "mGridSizeString='" + mGridSizeString + '\''
+ ", mNumHotseat=" + mNumHotseat
+ ", mDeviceType=" + mDeviceType
+ + ", mDbFile=" + mDbFile
+ '}';
}
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index 74b0a6f..0369b21 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -22,7 +22,6 @@
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
@@ -104,13 +103,16 @@
* Check given a new IDP, if migration is necessary.
*/
public static boolean needsToMigrate(Context context, InvariantDeviceProfile idp) {
- DeviceGridState idpGridState = new DeviceGridState(idp);
- DeviceGridState contextGridState = new DeviceGridState(context);
- boolean needsToMigrate = !idpGridState.isCompatible(contextGridState);
+ return needsToMigrate(new DeviceGridState(context), new DeviceGridState(idp));
+ }
+
+ private static boolean needsToMigrate(
+ DeviceGridState srcDeviceState, DeviceGridState destDeviceState) {
+ boolean needsToMigrate = !destDeviceState.isCompatible(srcDeviceState);
// TODO(b/198965093): Revert this change after bug is fixed
if (needsToMigrate) {
- Log.d("b/198965093", "Migration is needed. idpGridState: " + idpGridState
- + ", contextGridState: " + contextGridState);
+ Log.d("b/198965093", "Migration is needed. destDeviceState: " + destDeviceState
+ + ", srcDeviceState: " + srcDeviceState);
}
return needsToMigrate;
}
@@ -143,23 +145,26 @@
idp = LauncherAppState.getIDP(context);
}
- if (!needsToMigrate(context, idp)) {
+ DeviceGridState srcDeviceState = new DeviceGridState(context);
+ DeviceGridState destDeviceState = new DeviceGridState(idp);
+ if (!needsToMigrate(srcDeviceState, destDeviceState)) {
return true;
}
- SharedPreferences prefs = Utilities.getPrefs(context);
HashSet<String> validPackages = getValidPackages(context);
if (migrateForPreview) {
if (!LauncherSettings.Settings.call(
context.getContentResolver(),
- LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW, idp.dbFile).getBoolean(
+ LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW,
+ destDeviceState.getDbFile()).getBoolean(
LauncherSettings.Settings.EXTRA_VALUE)) {
return false;
}
} else if (!LauncherSettings.Settings.call(
context.getContentResolver(),
- LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER).getBoolean(
+ LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER,
+ destDeviceState.getDbFile()).getBoolean(
LauncherSettings.Settings.EXTRA_VALUE)) {
return false;
}
@@ -179,10 +184,10 @@
: LauncherSettings.Favorites.TABLE_NAME,
context, validPackages);
- Point targetSize = new Point(idp.numColumns, idp.numRows);
+ Point targetSize = new Point(destDeviceState.getColumns(), destDeviceState.getRows());
GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(context, t.getDb(),
- srcReader, destReader, idp.numDatabaseHotseatIcons, targetSize);
- task.migrate(idp);
+ srcReader, destReader, destDeviceState.getNumHotseat(), targetSize);
+ task.migrate(srcDeviceState, destDeviceState);
if (!migrateForPreview) {
dropTable(t.getDb(), LauncherSettings.Favorites.TMP_TABLE);
@@ -200,13 +205,13 @@
if (!migrateForPreview) {
// Save current configuration, so that the migration does not run again.
- new DeviceGridState(idp).writeToPrefs(context);
+ destDeviceState.writeToPrefs(context);
}
}
}
@VisibleForTesting
- protected boolean migrate(InvariantDeviceProfile idp) {
+ protected boolean migrate(DeviceGridState srcDeviceState, DeviceGridState destDeviceState) {
if (mHotseatDiff.isEmpty() && mWorkspaceDiff.isEmpty()) {
return false;
}
@@ -228,8 +233,6 @@
boolean preservePages = false;
if (screens.isEmpty() && FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC.get()) {
- DeviceGridState srcDeviceState = new DeviceGridState(mContext);
- DeviceGridState destDeviceState = new DeviceGridState(idp);
preservePages = destDeviceState.compareTo(srcDeviceState) >= 0
&& destDeviceState.getColumns() - srcDeviceState.getColumns() <= 2;
}
diff --git a/src/com/android/launcher3/search/SearchAlgorithm.java b/src/com/android/launcher3/search/SearchAlgorithm.java
index 8d85021..96a15f4 100644
--- a/src/com/android/launcher3/search/SearchAlgorithm.java
+++ b/src/com/android/launcher3/search/SearchAlgorithm.java
@@ -25,14 +25,13 @@
/**
* Performs search and sends the result to {@link SearchCallback}.
*/
- void doSearch(String query, int cursorLocation, SearchCallback<T> callback);
+ void doSearch(String query, SearchCallback<T> callback);
/**
* Performs search with {@code query} and the {@code suggestedQueries}/
*/
- default void doSearch(String query, int cursorLocation, String[] suggestedQueries,
- SearchCallback<T> callback) {
- doSearch(query, cursorLocation, callback);
+ default void doSearch(String query, String[] suggestedQueries, SearchCallback<T> callback) {
+ doSearch(query, callback);
}
/**
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index afeef58..faf5817 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -82,6 +82,10 @@
public static final String REQUEST_IS_LAUNCHER_INITIALIZED = "is-launcher-initialized";
public static final String REQUEST_FREEZE_APP_LIST = "freeze-app-list";
public static final String REQUEST_UNFREEZE_APP_LIST = "unfreeze-app-list";
+ public static final String REQUEST_ENABLE_MANUAL_TASKBAR_STASHING = "enable-taskbar-stashing";
+ public static final String REQUEST_DISABLE_MANUAL_TASKBAR_STASHING = "disable-taskbar-stashing";
+ public static final String REQUEST_UNSTASH_TASKBAR_IF_STASHED = "unstash-taskbar-if-stashed";
+ public static final String REQUEST_STASHED_TASKBAR_HEIGHT = "stashed-taskbar-height";
public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags";
public static final String REQUEST_APPS_LIST_SCROLL_Y = "apps-list-scroll-y";
public static final String REQUEST_WIDGETS_SCROLL_Y = "widgets-scroll-y";
@@ -96,6 +100,10 @@
public static final String REQUEST_GET_HAD_NONTEST_EVENTS = "get-had-nontest-events";
public static final String REQUEST_STOP_EVENT_LOGGING = "stop-event-logging";
public static final String REQUEST_CLEAR_DATA = "clear-data";
+ public static final String REQUEST_USE_TEST_WORKSPACE_LAYOUT = "use-test-workspace-layout";
+ public static final String REQUEST_USE_DEFAULT_WORKSPACE_LAYOUT =
+ "use-default-workspace-layout";
+ public static final String REQUEST_HOTSEAT_ICON_NAMES = "get-hotseat-icon-names";
public static final String REQUEST_IS_TABLET = "is-tablet";
public static final String REQUEST_IS_TWO_PANELS = "is-two-panel";
public static final String REQUEST_START_DRAG_THRESHOLD = "start-drag-threshold";
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 00a0050..b12574f 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -163,9 +163,8 @@
widthUsed = Math.max(widthUsed, minUsedWidth);
}
- int heightUsed = mInsets.top + deviceProfile.edgeMarginPx;
measureChildWithMargins(mContent, widthMeasureSpec,
- widthUsed, heightMeasureSpec, heightUsed);
+ widthUsed, heightMeasureSpec, deviceProfile.bottomSheetTopPadding);
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
MeasureSpec.getSize(heightMeasureSpec));
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 6e97774..341cb5c 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -96,8 +96,6 @@
private static final String KEY_WIDGETS_EDUCATION_DIALOG_SEEN =
"launcher.widgets_education_dialog_seen";
- private final Rect mInsets = new Rect();
-
private final UserManagerState mUserManagerState = new UserManagerState();
private final boolean mHasWorkProfile;
diff --git a/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithm.java b/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithm.java
index 1e58154..9be3b5f 100644
--- a/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithm.java
+++ b/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithm.java
@@ -48,8 +48,7 @@
}
@Override
- public void doSearch(String query, int cursorLocation,
- SearchCallback<WidgetsListBaseEntry> callback) {
+ public void doSearch(String query, SearchCallback<WidgetsListBaseEntry> callback) {
ArrayList<WidgetsListBaseEntry> result = getFilteredWidgets(mDataProvider, query);
mResultHandler.post(() -> callback.onSearchResult(query, result));
}
diff --git a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java
index 35a2dbd..2751a52 100644
--- a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java
+++ b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java
@@ -71,7 +71,7 @@
} else {
mSearchAlgorithm.cancel(/* interruptActiveRequests= */ false);
mSearchModeListener.enterSearchMode();
- mSearchAlgorithm.doSearch(mQuery, mInput.getSelectionStart(), this);
+ mSearchAlgorithm.doSearch(mQuery, this);
mCancelButton.setVisibility(VISIBLE);
}
}
diff --git a/src_build_config/com/android/launcher3/BuildConfig.java b/src_build_config/com/android/launcher3/BuildConfig.java
index 49aadf6..9a81d3f 100644
--- a/src_build_config/com/android/launcher3/BuildConfig.java
+++ b/src_build_config/com/android/launcher3/BuildConfig.java
@@ -17,6 +17,11 @@
package com.android.launcher3;
public final class BuildConfig {
- public static final String APPLICATION_ID = "com.android.launcher3";
- public static final boolean DEBUG = false;
+ public static final String APPLICATION_ID = "com.android.launcher3";
+ public static final boolean DEBUG = false;
+ /**
+ * Flag to state if the QSB is on the first screen and placed on the top,
+ * this can be overwritten in other launchers with a different value, if needed.
+ */
+ public static final boolean QSB_ON_FIRST_SCREEN = true;
}
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index b4cd773..9cc3aed 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -94,7 +94,6 @@
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
- <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
@@ -138,6 +137,7 @@
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<intent-filter>
<action android:name="com.android.launcher3.intent.action.test_shortcut"/>
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.kt b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.kt
index 239e092..345016b 100644
--- a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.kt
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.kt
@@ -125,7 +125,7 @@
idp.numDatabaseHotseatIcons,
Point(idp.numColumns, idp.numRows)
)
- task.migrate(idp)
+ task.migrate(DeviceGridState(context), DeviceGridState(idp))
// Check hotseat items
var c = context.contentResolver.query(
@@ -205,7 +205,7 @@
idp.numDatabaseHotseatIcons,
Point(idp.numColumns, idp.numRows)
)
- task.migrate(idp)
+ task.migrate(DeviceGridState(context), DeviceGridState(idp))
// Check hotseat items
val c = context.contentResolver.query(
@@ -260,7 +260,7 @@
idp.numDatabaseHotseatIcons,
Point(idp.numColumns, idp.numRows)
)
- task.migrate(idp)
+ task.migrate(DeviceGridState(context), DeviceGridState(idp))
// Check hotseat items
val c = context.contentResolver.query(
@@ -325,7 +325,7 @@
idp.numDatabaseHotseatIcons,
Point(idp.numColumns, idp.numRows)
)
- task.migrate(idp)
+ task.migrate(DeviceGridState(context), DeviceGridState(idp))
// Get workspace items
val c = context.contentResolver.query(
@@ -385,7 +385,7 @@
idp.numDatabaseHotseatIcons,
Point(idp.numColumns, idp.numRows)
)
- task.migrate(idp)
+ task.migrate(DeviceGridState(context), DeviceGridState(idp))
// Get workspace items
val c = context.contentResolver.query(
@@ -446,7 +446,7 @@
idp.numDatabaseHotseatIcons,
Point(idp.numColumns, idp.numRows)
)
- task.migrate(idp)
+ task.migrate(DeviceGridState(context), DeviceGridState(idp))
// Get workspace items
val c = context.contentResolver.query(
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index c99c4f1..136f115 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -567,6 +567,7 @@
ordinal == TestProtocol.OVERVIEW_STATE_ORDINAL);
break;
}
+ case TASKBAR_ALL_APPS:
case LAUNCHED_APP: {
assertTrue("Launcher is resumed in state: " + expectedContainerType,
!isResumed);
@@ -580,9 +581,10 @@
}
} else {
assertTrue(
- "Container type is not LAUNCHED_APP or FALLBACK_OVERVIEW: "
- + expectedContainerType,
+ "Container type is not LAUNCHED_APP, TASKBAR_ALL_APPS "
+ + "or FALLBACK_OVERVIEW: " + expectedContainerType,
expectedContainerType == ContainerType.LAUNCHED_APP
+ || expectedContainerType == ContainerType.TASKBAR_ALL_APPS
|| expectedContainerType == ContainerType.FALLBACK_OVERVIEW);
}
}
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 61ec8bd..73e9823 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -109,7 +109,7 @@
launcher -> assertNotNull("Launcher internal state didn't switch to Showing Menu",
launcher.getOptionsPopup()));
// Check that pressHome works when the menu is shown.
- mLauncher.pressHome();
+ mLauncher.goHome();
}
@Test
@@ -121,7 +121,7 @@
} finally {
allApps.unfreeze();
}
- mLauncher.pressHome();
+ mLauncher.goHome();
}
public static void runAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) {
@@ -273,7 +273,7 @@
executeOnLauncher(launcher -> assertTrue("Flinging backward didn't scroll widgets",
getWidgetsScroll(launcher) < flingForwardY));
- mLauncher.pressHome();
+ mLauncher.goHome();
waitForLauncherCondition("Widgets were not closed",
launcher -> getWidgetsView(launcher) == null);
}
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 0c1f0cd..9fb52fc 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -169,7 +169,7 @@
}
// Go back to home
- mLauncher.pressHome();
+ mLauncher.goHome();
Wait.atMost("", new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT,
mLauncher);
}
diff --git a/tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java b/tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
index 945c992..d812ab0 100644
--- a/tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
@@ -163,7 +163,7 @@
mCameraContentEntry, mClockHeaderEntry, mClockContentEntry))
.when(mDataProvider)
.getAllWidgets();
- mSimpleWidgetsSearchAlgorithm.doSearch("Ca", 2, mSearchCallback);
+ mSimpleWidgetsSearchAlgorithm.doSearch("Ca", mSearchCallback);
MAIN_EXECUTOR.submit(() -> { }).get();
verify(mSearchCallback).onSearchResult(
matches("Ca"), argThat(a -> a != null && !a.isEmpty()));
diff --git a/tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java b/tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
index cff601e..583d37f 100644
--- a/tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
@@ -86,7 +86,7 @@
public void afterTextChanged_shouldDoSearch() {
mEditText.setText("abc");
- verify(mSearchAlgorithm).doSearch(eq("abc"), 3, any());
+ verify(mSearchAlgorithm).doSearch(eq("abc"), any());
}
@Test
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 3658f41..bfb115d 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -74,7 +74,7 @@
LauncherInstrumentation.log("hasClickableIcon: icon has insufficient height");
return false;
}
- if (iconCenterInSearchBox(allAppsContainer, icon)) {
+ if (hasSearchBox() && iconCenterInSearchBox(allAppsContainer, icon)) {
LauncherInstrumentation.log("hasClickableIcon: icon center is under search box");
return false;
}
@@ -107,7 +107,7 @@
final UiObject2 allAppsContainer = verifyActiveContainer();
final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer,
"apps_list_view");
- final UiObject2 searchBox = getSearchBox(allAppsContainer);
+ final UiObject2 searchBox = hasSearchBox() ? getSearchBox(allAppsContainer) : null;
int deviceHeight = mLauncher.getRealDisplaySize().y;
int bottomGestureStartOnScreen = mLauncher.getBottomGestureStartOnScreen();
@@ -128,8 +128,10 @@
mLauncher.getVisibleBounds(icon).top
< bottomGestureStartOnScreen)
.collect(Collectors.toList()),
- mLauncher.getVisibleBounds(searchBox).bottom
- - mLauncher.getVisibleBounds(allAppsContainer).top);
+ hasSearchBox()
+ ? mLauncher.getVisibleBounds(searchBox).bottom
+ - mLauncher.getVisibleBounds(allAppsContainer).top
+ : 0);
verifyActiveContainer();
final int newScroll = getAllAppsScroll();
mLauncher.assertTrue(
@@ -173,18 +175,21 @@
return appIcon;
}
+ @NonNull
protected abstract AppIcon createAppIcon(UiObject2 icon);
+ protected abstract boolean hasSearchBox();
+
private void scrollBackToBeginning() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to scroll back in all apps")) {
LauncherInstrumentation.log("Scrolling to the beginning");
final UiObject2 allAppsContainer = verifyActiveContainer();
- final UiObject2 searchBox = getSearchBox(allAppsContainer);
+ final UiObject2 searchBox = hasSearchBox() ? getSearchBox(allAppsContainer) : null;
int attempts = 0;
- final Rect margins =
- new Rect(0, mLauncher.getVisibleBounds(searchBox).bottom + 1, 0, 5);
+ final Rect margins = new Rect(
+ 0, hasSearchBox() ? mLauncher.getVisibleBounds(searchBox).bottom + 1 : 0, 0, 5);
for (int scroll = getAllAppsScroll();
scroll != 0;
@@ -196,7 +201,11 @@
++attempts <= MAX_SCROLL_ATTEMPTS);
mLauncher.scroll(
- allAppsContainer, Direction.UP, margins, 12, false);
+ allAppsContainer,
+ Direction.UP,
+ margins,
+ /* steps= */ 12,
+ /* slowDown= */ false);
}
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled up")) {
@@ -225,7 +234,11 @@
final UiObject2 allAppsContainer = verifyActiveContainer();
// Start the gesture in the center to avoid starting at elements near the top.
mLauncher.scroll(
- allAppsContainer, Direction.DOWN, new Rect(0, 0, 0, mHeight / 2), 10, false);
+ allAppsContainer,
+ Direction.DOWN,
+ new Rect(0, 0, 0, mHeight / 2),
+ /* steps= */ 10,
+ /* slowDown= */ false);
verifyActiveContainer();
}
}
@@ -240,7 +253,11 @@
final UiObject2 allAppsContainer = verifyActiveContainer();
// Start the gesture in the center, for symmetry with forward.
mLauncher.scroll(
- allAppsContainer, Direction.UP, new Rect(0, mHeight / 2, 0, 0), 10, false);
+ allAppsContainer,
+ Direction.UP,
+ new Rect(0, mHeight / 2, 0, 0),
+ /* steps= */ 10,
+ /*slowDown= */ false);
verifyActiveContainer();
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java b/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
new file mode 100644
index 0000000..5164025
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 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.launcher3.tapl;
+
+import androidx.annotation.NonNull;
+import androidx.test.uiautomator.UiObject2;
+
+/**
+ * Operations on AllApps opened from the Taskbar.
+ */
+public class AllAppsFromTaskbar extends AllApps {
+
+ AllAppsFromTaskbar(LauncherInstrumentation launcher) {
+ super(launcher);
+ }
+
+ @Override
+ protected LauncherInstrumentation.ContainerType getContainerType() {
+ return LauncherInstrumentation.ContainerType.TASKBAR_ALL_APPS;
+ }
+
+ @NonNull
+ @Override
+ public TaskbarAppIcon getAppIcon(String appName) {
+ return (TaskbarAppIcon) super.getAppIcon(appName);
+ }
+
+ @NonNull
+ @Override
+ protected TaskbarAppIcon createAppIcon(UiObject2 icon) {
+ return new TaskbarAppIcon(mLauncher, icon);
+ }
+
+ @Override
+ protected boolean hasSearchBox() {
+ return false;
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index bef242c..e28f0af 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -51,7 +51,7 @@
public AppIconMenu openMenu() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
return createMenu(mLauncher.clickAndGet(
- mObject, "popup_container", getLongClickEvent()));
+ mObject, /* resName= */ "popup_container", getLongClickEvent()));
}
}
@@ -61,7 +61,7 @@
public AppIconMenu openDeepShortcutMenu() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
return createMenu(mLauncher.clickAndGet(
- mObject, "deep_shortcuts_container", getLongClickEvent()));
+ mObject, /* resName= */ "deep_shortcuts_container", getLongClickEvent()));
}
}
@@ -73,8 +73,8 @@
}
@Override
- protected String getLongPressIndicator() {
- return "popup_container";
+ protected void waitForLongPressConfirmation() {
+ mLauncher.waitForLauncherObject("popup_container");
}
@Override
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
index a6a1531..5cf5aba 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
@@ -41,8 +41,8 @@
}
@Override
- protected String getLongPressIndicator() {
- return "drop_target_bar";
+ protected void waitForLongPressConfirmation() {
+ mLauncher.waitForLauncherObject("drop_target_bar");
}
@Override
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
index 2e00d59..c275f3b 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
@@ -35,8 +35,14 @@
return (AllAppsAppIcon) super.getAppIcon(appName);
}
+ @NonNull
@Override
protected HomeAppIcon createAppIcon(UiObject2 icon) {
return new AllAppsAppIcon(mLauncher, icon);
}
+
+ @Override
+ protected boolean hasSearchBox() {
+ return true;
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java b/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java
index baabe12..71d8ba9 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java
@@ -51,8 +51,7 @@
() -> {
final Rect bounds = target.getDropLocationBounds();
return new Point(bounds.centerX(), bounds.centerY());
- },
- getLongPressIndicator());
+ });
FolderIcon result = target.getTargetFolder(dropBounds);
mLauncher.assertTrue("Can't find the target folder.", result != null);
return result;
@@ -115,8 +114,12 @@
String.format("want to drag the icon to cell(%d, %d)", cellX, cellY))
) {
final Supplier<Point> dest = () -> Workspace.getCellCenter(mLauncher, cellX, cellY);
- Workspace.dragIconToWorkspace(mLauncher, this, dest, getLongPressIndicator(),
- () -> addExpectedEventsForLongClick(), null);
+ Workspace.dragIconToWorkspace(
+ mLauncher,
+ /* launchable= */ this,
+ dest,
+ () -> addExpectedEventsForLongClick(),
+ /*expectDropEvents= */ null);
try (LauncherInstrumentation.Closable ignore = mLauncher.addContextLayer("dragged")) {
WorkspaceAppIcon appIcon =
(WorkspaceAppIcon) mLauncher.getWorkspace().getWorkspaceAppIcon(mAppName);
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index a28eac6..45a0196 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -97,8 +97,7 @@
return new LaunchedAppState(mLauncher);
}
- Point startDrag(long downTime, String longPressIndicator,
- Runnable expectLongClickEvents, boolean runToSpringLoadedState) {
+ Point startDrag(long downTime, Runnable expectLongClickEvents, boolean runToSpringLoadedState) {
final Point iconCenter = getObject().getVisibleCenter();
final Point dragStartCenter = new Point(iconCenter.x,
iconCenter.y - getStartDragThreshold());
@@ -108,7 +107,6 @@
downTime,
iconCenter,
dragStartCenter,
- longPressIndicator,
expectLongClickEvents),
SPRING_LOADED_STATE_ORDINAL, "long-pressing and triggering drag start");
} else {
@@ -116,7 +114,6 @@
downTime,
iconCenter,
dragStartCenter,
- longPressIndicator,
expectLongClickEvents);
}
@@ -125,21 +122,43 @@
}
/**
+ * Waits for a confirmation that a long press has successfully been triggered.
+ *
+ * This method waits for a view to either appear or disappear to confirm that the long press
+ * has been triggered and fails if no confirmation is received before the default timeout.
+ */
+ protected abstract void waitForLongPressConfirmation();
+
+ /**
* Drags this Launchable a short distance before starting a full drag.
*
* This is necessary for shortcuts, which require being dragged beyond a threshold to close
* their container and start drag callbacks.
*/
- private void movePointerForStartDrag(long downTime, Point iconCenter, Point dragStartCenter,
- String longPressIndicator, Runnable expectLongClickEvents) {
- mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN,
- iconCenter, LauncherInstrumentation.GestureScope.INSIDE);
+ private void movePointerForStartDrag(
+ long downTime,
+ Point iconCenter,
+ Point dragStartCenter,
+ Runnable expectLongClickEvents) {
+ mLauncher.sendPointer(
+ downTime,
+ downTime,
+ MotionEvent.ACTION_DOWN,
+ iconCenter,
+ LauncherInstrumentation.GestureScope.INSIDE);
LauncherInstrumentation.log("movePointerForStartDrag: sent down");
expectLongClickEvents.run();
- mLauncher.waitForLauncherObject(longPressIndicator);
+ waitForLongPressConfirmation();
LauncherInstrumentation.log("movePointerForStartDrag: indicator");
- mLauncher.movePointer(iconCenter, dragStartCenter, DEFAULT_DRAG_STEPS, false,
- downTime, downTime, true, LauncherInstrumentation.GestureScope.INSIDE);
+ mLauncher.movePointer(
+ iconCenter,
+ dragStartCenter,
+ DEFAULT_DRAG_STEPS,
+ /* isDecelerating= */ false,
+ downTime,
+ downTime,
+ /* slowDown= */ true,
+ LauncherInstrumentation.GestureScope.INSIDE);
}
private int getStartDragThreshold() {
@@ -148,6 +167,4 @@
}
protected abstract void addExpectedEventsForLongClick();
-
- protected abstract String getLongPressIndicator();
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
index 3f1be80..2033a42 100644
--- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
+++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
@@ -16,11 +16,27 @@
package com.android.launcher3.tapl;
+import static com.android.launcher3.testing.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING;
+import static com.android.launcher3.testing.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING;
+import static com.android.launcher3.testing.TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+
+import androidx.test.uiautomator.By;
+
+import com.android.launcher3.testing.TestProtocol;
+
/**
* Background state operations specific to when an app has been launched.
*/
public final class LaunchedAppState extends Background {
+ // More drag steps than Launchables to give the window manager time to register the drag.
+ private static final int DEFAULT_DRAG_STEPS = 35;
+
LaunchedAppState(LauncherInstrumentation launcher) {
super(launcher);
}
@@ -29,4 +45,126 @@
protected LauncherInstrumentation.ContainerType getContainerType() {
return LauncherInstrumentation.ContainerType.LAUNCHED_APP;
}
+
+ /**
+ * Returns the taskbar.
+ *
+ * The taskbar must already be visible when calling this method.
+ */
+ public Taskbar getTaskbar() {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to get the taskbar")) {
+ mLauncher.waitForLauncherObject("taskbar_view");
+
+ return new Taskbar(mLauncher);
+ }
+ }
+
+ /**
+ * Returns the Taskbar in a visible state.
+ *
+ * The taskbar must already be hidden when calling this method.
+ */
+ public Taskbar showTaskbar() {
+ mLauncher.getTestInfo(REQUEST_ENABLE_MANUAL_TASKBAR_STASHING);
+
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "want to show the taskbar")) {
+ mLauncher.waitUntilLauncherObjectGone("taskbar_view");
+
+ final long downTime = SystemClock.uptimeMillis();
+ final int unstashTargetY = mLauncher.getRealDisplaySize().y
+ - (mLauncher.getTestInfo(REQUEST_STASHED_TASKBAR_HEIGHT)
+ .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD) / 2);
+ final Point unstashTarget = new Point(
+ mLauncher.getRealDisplaySize().x / 2, unstashTargetY);
+
+ mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, unstashTarget,
+ LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER);
+ LauncherInstrumentation.log("showTaskbar: sent down");
+
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) {
+ mLauncher.waitForLauncherObject("taskbar_view");
+ mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, unstashTarget,
+ LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER);
+
+ return new Taskbar(mLauncher);
+ }
+ } finally {
+ mLauncher.getTestInfo(REQUEST_DISABLE_MANUAL_TASKBAR_STASHING);
+ }
+ }
+
+ static void dragToSplitscreen(
+ LauncherInstrumentation launcher, Launchable launchable, String expectedNewPackageName,
+ String expectedExistingPackageName) {
+ try (LauncherInstrumentation.Closable c1 = launcher.addContextLayer(
+ "want to drag taskbar item to splitscreen")) {
+ final Point displaySize = launcher.getRealDisplaySize();
+ final Point endPoint = new Point(displaySize.x / 4, 3 * displaySize.y / 4);
+ final long downTime = SystemClock.uptimeMillis();
+ // Use mObject before starting drag since the system drag and drop moves the original
+ // view.
+ Point itemVisibleCenter = launchable.mObject.getVisibleCenter();
+ Rect itemVisibleBounds = launcher.getVisibleBounds(launchable.mObject);
+ String itemLabel = launchable.mObject.getText();
+
+ Point dragStart = launchable.startDrag(
+ downTime,
+ launchable::addExpectedEventsForLongClick,
+ /* runToSpringLoadedState= */ false);
+
+ try (LauncherInstrumentation.Closable c2 = launcher.addContextLayer(
+ "started item drag")) {
+ launcher.movePointer(
+ dragStart,
+ endPoint,
+ DEFAULT_DRAG_STEPS,
+ /* isDecelerating= */ true,
+ downTime,
+ SystemClock.uptimeMillis(),
+ /* slowDown= */ false,
+ LauncherInstrumentation.GestureScope.INSIDE);
+
+ try (LauncherInstrumentation.Closable c3 = launcher.addContextLayer(
+ "moved pointer to drop point")) {
+ dropDraggedItem(
+ launcher,
+ launchable,
+ expectedNewPackageName,
+ endPoint, downTime,
+ itemVisibleCenter,
+ itemVisibleBounds,
+ itemLabel,
+ expectedExistingPackageName);
+ }
+ }
+ }
+ }
+
+ private static void dropDraggedItem(
+ LauncherInstrumentation launcher, Launchable launchable, String expectedNewPackageName,
+ Point endPoint, long downTime, Point itemVisibleCenter, Rect itemVisibleBounds,
+ String itemLabel, String expectedExistingPackageName) {
+ LauncherInstrumentation.log("SplitscreenDragSource.dragToSplitscreen before drop "
+ + itemVisibleCenter + " in " + itemVisibleBounds);
+
+ launchable.executeAndWaitForWindowChange(() -> {
+ launcher.sendPointer(
+ downTime,
+ SystemClock.uptimeMillis(),
+ MotionEvent.ACTION_UP,
+ endPoint,
+ LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE_WITHOUT_PILFER);
+ LauncherInstrumentation.log("SplitscreenDragSource.dragToSplitscreen: after "
+ + "drop");
+ }, itemLabel, "dropping taskbar item");
+
+ try (LauncherInstrumentation.Closable c = launcher.addContextLayer("dropped item")) {
+ launchable.assertAppLaunched(itemLabel, By.pkg(expectedNewPackageName));
+ launcher.checkPackagesVisible(
+ new String[] {expectedNewPackageName, expectedExistingPackageName});
+ }
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index bbfbc55..8e0eb7b 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -84,6 +84,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
@@ -123,7 +124,8 @@
// Types for launcher containers that the user is interacting with. "Background" is a
// pseudo-container corresponding to inactive launcher covered by another app.
public enum ContainerType {
- WORKSPACE, HOME_ALL_APPS, OVERVIEW, WIDGETS, FALLBACK_OVERVIEW, LAUNCHED_APP
+ WORKSPACE, HOME_ALL_APPS, OVERVIEW, WIDGETS, FALLBACK_OVERVIEW, LAUNCHED_APP,
+ TASKBAR_ALL_APPS
}
public enum NavigationModel {ZERO_BUTTON, THREE_BUTTON}
@@ -167,6 +169,7 @@
private static final String OVERVIEW_RES_ID = "overview_panel";
private static final String WIDGETS_RES_ID = "primary_widgets_list_view";
private static final String CONTEXT_MENU_RES_ID = "popup_container";
+ private static final String TASKBAR_RES_ID = "taskbar_view";
public static final int WAIT_TIME_MS = 60000;
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
private static final String ANDROID_PACKAGE = "android";
@@ -500,6 +503,16 @@
}
}
+ void checkPackagesVisible(String[] expectedVisiblePackages) {
+ Set<String> actualVisiblePackages =
+ getVisiblePackagesStream().collect(Collectors.toSet());
+
+ for (String expectedVisible : expectedVisiblePackages) {
+ assertTrue("Expected package not visible: " + expectedVisible,
+ actualVisiblePackages.contains(expectedVisible));
+ }
+ }
+
private String getVisiblePackages() {
final String apps = getVisiblePackagesStream().collect(Collectors.joining(", "));
return !apps.isEmpty()
@@ -722,27 +735,41 @@
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
+ waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+
return waitForLauncherObject(WORKSPACE_RES_ID);
}
case WIDGETS: {
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
+ waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+
return waitForLauncherObject(WIDGETS_RES_ID);
}
+ case TASKBAR_ALL_APPS:
case HOME_ALL_APPS: {
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
+ waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+
return waitForLauncherObject(APPS_RES_ID);
}
case OVERVIEW: {
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
+ waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+
return waitForLauncherObject(OVERVIEW_RES_ID);
}
case FALLBACK_OVERVIEW: {
+ waitUntilLauncherObjectGone(APPS_RES_ID);
+ waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
+ waitUntilLauncherObjectGone(WIDGETS_RES_ID);
+ waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+
return waitForFallbackLauncherObject(OVERVIEW_RES_ID);
}
case LAUNCHED_APP: {
@@ -750,6 +777,12 @@
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
+
+ if (isTablet() && !isFallbackOverview()) {
+ waitForLauncherObject(TASKBAR_RES_ID);
+ } else {
+ waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+ }
return null;
}
default:
@@ -844,11 +877,22 @@
}
/**
+ * @deprecated use goHome().
* Presses nav bar home button.
*
* @return the Workspace object.
*/
+ @Deprecated
public Workspace pressHome() {
+ return goHome();
+ }
+
+ /**
+ * Presses nav bar home button.
+ *
+ * @return the Workspace object.
+ */
+ public Workspace goHome() {
try (LauncherInstrumentation.Closable e = eventsCheck();
LauncherInstrumentation.Closable c = addContextLayer("want to switch to home")) {
waitForLauncherInitialized();
@@ -863,8 +907,13 @@
setForcePauseTimeout(FORCE_PAUSE_TIMEOUT_MS);
final Point displaySize = getRealDisplaySize();
+ // The swipe up to home gesture starts from inside the launcher when the user is
+ // already home. Otherwise, the gesture can start inside the launcher process if the
+ // taskbar is visible.
boolean gestureStartFromLauncher = isTablet()
- ? !isLauncher3() || hasLauncherObject(WORKSPACE_RES_ID)
+ ? !isLauncher3()
+ || hasLauncherObject(WORKSPACE_RES_ID)
+ || hasLauncherObject(TASKBAR_RES_ID)
: isLauncherVisible();
// CLose floating views before going back to home.
@@ -1301,9 +1350,7 @@
}
void scrollToLastVisibleRow(
- UiObject2 container,
- Collection<UiObject2> items,
- int topPaddingInContainer) {
+ UiObject2 container, Collection<UiObject2> items, int topPaddingInContainer) {
final UiObject2 lowestItem = Collections.max(items, (i1, i2) ->
Integer.compare(getVisibleBounds(i1).top, getVisibleBounds(i2).top));
@@ -1326,8 +1373,8 @@
containerRect.height() - distance - bottomGestureMarginInContainer,
0,
bottomGestureMarginInContainer),
- 10,
- true);
+ /* steps= */ 10,
+ /* slowDown= */ true);
}
void scrollLeftByDistance(UiObject2 container, int distance) {
@@ -1650,6 +1697,29 @@
getTestInfo(TestProtocol.REQUEST_CLEAR_DATA);
}
+ /**
+ * Reloads the workspace with a test layout that includes the Test Activity app icon on the
+ * hotseat.
+ */
+ public void useTestWorkspaceLayoutOnReload() {
+ getTestInfo(TestProtocol.REQUEST_USE_TEST_WORKSPACE_LAYOUT);
+ }
+
+ /** Reloads the workspace with the default layout defined by the user's grid size selection. */
+ public void useDefaultWorkspaceLayoutOnReload() {
+ getTestInfo(TestProtocol.REQUEST_USE_DEFAULT_WORKSPACE_LAYOUT);
+ }
+
+ /** Shows the taskbar if it is hidden, otherwise does nothing. */
+ public void showTaskbarIfHidden() {
+ getTestInfo(TestProtocol.REQUEST_UNSTASH_TASKBAR_IF_STASHED);
+ }
+
+ public List<String> getHotseatIconNames() {
+ return getTestInfo(TestProtocol.REQUEST_HOTSEAT_ICON_NAMES)
+ .getStringArrayList(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
private String[] getActivities() {
return getTestInfo(TestProtocol.REQUEST_GET_ACTIVITIES)
.getStringArray(TestProtocol.TEST_INFO_RESPONSE_FIELD);
diff --git a/tests/tapl/com/android/launcher3/tapl/SplitscreenDragSource.java b/tests/tapl/com/android/launcher3/tapl/SplitscreenDragSource.java
new file mode 100644
index 0000000..ce1c3c0
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/SplitscreenDragSource.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 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.launcher3.tapl;
+
+/** Launchable that can serve as a source for dragging and dropping to splitscreen. */
+interface SplitscreenDragSource {
+
+ /**
+ * Drags this app icon to the left (landscape) or bottom (portrait) of the screen, launching it
+ * in splitscreen.
+ *
+ * @param expectedNewPackageName package name of the app being dragged
+ * @param expectedExistingPackageName package name of the already-launched app
+ */
+ default void dragToSplitscreen(
+ String expectedNewPackageName, String expectedExistingPackageName) {
+ Launchable launchable = getLaunchable();
+ LauncherInstrumentation launcher = launchable.mLauncher;
+ try (LauncherInstrumentation.Closable e = launcher.eventsCheck()) {
+ LaunchedAppState.dragToSplitscreen(
+ launcher, launchable, expectedNewPackageName, expectedExistingPackageName);
+ }
+ }
+
+ Launchable getLaunchable();
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/Taskbar.java b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
new file mode 100644
index 0000000..b5a08c3
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2022 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.launcher3.tapl;
+
+import static com.android.launcher3.testing.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING;
+import static com.android.launcher3.testing.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING;
+
+import android.graphics.Point;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.view.MotionEvent;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiObject2;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Operations on the Taskbar from LaunchedApp.
+ */
+public final class Taskbar {
+
+ private final LauncherInstrumentation mLauncher;
+
+ Taskbar(LauncherInstrumentation launcher) {
+ mLauncher = launcher;
+ }
+
+ /**
+ * Returns an app icon with the given name. This fails if the icon is not found.
+ */
+ @NonNull
+ public TaskbarAppIcon getAppIcon(String appName) {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to get a taskbar icon")) {
+ return new TaskbarAppIcon(mLauncher, mLauncher.waitForObjectInContainer(
+ mLauncher.waitForLauncherObject("taskbar_view"),
+ AppIcon.getAppIconSelector(appName, mLauncher)));
+ }
+ }
+
+ /**
+ * Hides this taskbar.
+ *
+ * The taskbar must already be visible when calling this method.
+ */
+ public void hide() {
+ mLauncher.getTestInfo(REQUEST_ENABLE_MANUAL_TASKBAR_STASHING);
+
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to hide the taskbar");
+ LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ mLauncher.waitForLauncherObject("taskbar_view");
+
+ final long downTime = SystemClock.uptimeMillis();
+ Point stashTarget = new Point(
+ mLauncher.getRealDisplaySize().x - 1, mLauncher.getRealDisplaySize().y - 1);
+
+ mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, stashTarget,
+ LauncherInstrumentation.GestureScope.INSIDE);
+ LauncherInstrumentation.log("hideTaskbar: sent down");
+
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) {
+ mLauncher.waitUntilLauncherObjectGone("taskbar_view");
+ mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, stashTarget,
+ LauncherInstrumentation.GestureScope.INSIDE);
+ }
+ } finally {
+ mLauncher.getTestInfo(REQUEST_DISABLE_MANUAL_TASKBAR_STASHING);
+ }
+ }
+
+ /**
+ * Opens the Taskbar all apps page.
+ */
+ public AllAppsFromTaskbar openAllApps() {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to open taskbar all apps");
+ LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+
+ mLauncher.clickLauncherObject(mLauncher.waitForObjectInContainer(
+ mLauncher.waitForLauncherObject("taskbar_view"), getAllAppsButtonSelector()));
+
+ return new AllAppsFromTaskbar(mLauncher);
+ }
+ }
+
+ /** Returns a list of app icon names on the Taskbar */
+ public List<String> getIconNames() {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to get all taskbar icons")) {
+ return mLauncher.waitForObjectsInContainer(
+ mLauncher.waitForLauncherObject("taskbar_view"),
+ AppIcon.getAnyAppIconSelector())
+ .stream()
+ .map(UiObject2::getText)
+ .filter(text -> !TextUtils.isEmpty(text)) // Filter out the all apps button
+ .collect(Collectors.toList());
+ }
+ }
+
+ private static BySelector getAllAppsButtonSelector() {
+ // Look for an icon with no text
+ return By.clazz(TextView.class).text("");
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/TaskbarAppIcon.java b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIcon.java
new file mode 100644
index 0000000..099acd4
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIcon.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 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.launcher3.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+import java.util.regex.Pattern;
+
+/**
+ * App icon specifically on the Taskbar.
+ */
+public final class TaskbarAppIcon extends AppIcon implements SplitscreenDragSource {
+
+ private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onTaskbarItemLongClick");
+
+ TaskbarAppIcon(LauncherInstrumentation launcher, UiObject2 icon) {
+ super(launcher, icon);
+ }
+
+ @Override
+ protected Pattern getLongClickEvent() {
+ return LONG_CLICK_EVENT;
+ }
+
+ @Override
+ public TaskbarAppIconMenu openDeepShortcutMenu() {
+ return (TaskbarAppIconMenu) super.openDeepShortcutMenu();
+ }
+
+ @Override
+ protected TaskbarAppIconMenu createMenu(UiObject2 menu) {
+ return new TaskbarAppIconMenu(mLauncher, menu);
+ }
+
+ @Override
+ public Launchable getLaunchable() {
+ return this;
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenu.java b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenu.java
new file mode 100644
index 0000000..1f137c5
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenu.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 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.launcher3.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+/**
+ * Context menu of a Taskbar app icon.
+ */
+public final class TaskbarAppIconMenu extends AppIconMenu {
+
+ TaskbarAppIconMenu(LauncherInstrumentation launcher, UiObject2 deepShortcutsContainer) {
+ super(launcher, deepShortcutsContainer);
+ }
+
+ @Override
+ public TaskbarAppIconMenuItem getMenuItem(String shortcutText) {
+ return (TaskbarAppIconMenuItem) super.getMenuItem(shortcutText);
+ }
+
+ @Override
+ protected TaskbarAppIconMenuItem createMenuItem(UiObject2 menuItem) {
+ return new TaskbarAppIconMenuItem(mLauncher, menuItem);
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenuItem.java b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenuItem.java
new file mode 100644
index 0000000..69a8a08
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenuItem.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 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.launcher3.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+import com.android.launcher3.testing.TestProtocol;
+
+import java.util.regex.Pattern;
+
+/**
+ * Menu item in a Taskbar app icon menu.
+ */
+public final class TaskbarAppIconMenuItem extends AppIconMenuItem implements SplitscreenDragSource {
+
+ private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onTaskbarItemLongClick");
+
+ TaskbarAppIconMenuItem(
+ LauncherInstrumentation launcher, UiObject2 shortcut) {
+ super(launcher, shortcut);
+ }
+
+ @Override
+ protected void addExpectedEventsForLongClick() {
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT);
+ }
+
+ @Override
+ protected void waitForLongPressConfirmation() {
+ // On long-press, the popup container closes and the system drag-and-drop begins. This
+ // only leaves launcher views that were previously visible.
+ mLauncher.waitUntilLauncherObjectGone("popup_container");
+ }
+
+ @Override
+ protected String launchableType() {
+ return "taskbar app icon menu item";
+ }
+
+ @Override
+ public Launchable getLaunchable() {
+ return this;
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/Widget.java b/tests/tapl/com/android/launcher3/tapl/Widget.java
index 73e9830..2346249 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widget.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widget.java
@@ -39,8 +39,8 @@
}
@Override
- protected String getLongPressIndicator() {
- return "drop_target_bar";
+ protected void waitForLongPressConfirmation() {
+ mLauncher.waitForLauncherObject("drop_target_bar");
}
@Override
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 3bc5389..fee4490 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -205,7 +205,6 @@
mLauncher,
homeAppIcon,
new Point(targetX, mLauncher.getVisibleBounds(workspace).centerY()),
- "popup_container",
false,
false,
() -> mLauncher.expectEvent(
@@ -244,11 +243,11 @@
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"removing app icon from workspace")) {
dragIconToWorkspace(
- mLauncher, homeAppIcon,
+ mLauncher,
+ homeAppIcon,
() -> getDropPointFromDropTargetBar(mLauncher, DELETE_TARGET_TEXT_ID),
- homeAppIcon.getLongPressIndicator(),
() -> mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
- null);
+ /* expectDropEvents= */ null);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"dragged the app to the drop bar")) {
@@ -274,11 +273,11 @@
try (LauncherInstrumentation.Closable c = launcher.addContextLayer(
"uninstalling app icon")) {
dragIconToWorkspace(
- launcher, homeAppIcon,
+ launcher,
+ homeAppIcon,
() -> getDropPointFromDropTargetBar(launcher, UNINSTALL_TARGET_TEXT_ID),
- homeAppIcon.getLongPressIndicator(),
expectLongClickEvents,
- null);
+ /* expectDropEvents= */null);
launcher.waitUntilLauncherObjectGone(DROP_BAR_RES_ID);
@@ -345,15 +344,15 @@
}
static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable,
- Point dest, String longPressIndicator, boolean startsActivity, boolean isWidgetShortcut,
+ Point dest, boolean startsActivity, boolean isWidgetShortcut,
Runnable expectLongClickEvents) {
Runnable expectDropEvents = null;
if (startsActivity || isWidgetShortcut) {
expectDropEvents = () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN,
LauncherInstrumentation.EVENT_START);
}
- dragIconToWorkspace(launcher, launchable, () -> dest, longPressIndicator,
- expectLongClickEvents, expectDropEvents);
+ dragIconToWorkspace(
+ launcher, launchable, () -> dest, expectLongClickEvents, expectDropEvents);
}
/**
@@ -361,22 +360,27 @@
* (There is no slow down time before drop event)
* This function expects the launchable is inside the workspace and there is no drop event.
*/
- static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable,
- Supplier<Point> destSupplier, String longPressIndicator) {
- dragIconToWorkspace(launcher, launchable, destSupplier, longPressIndicator,
- () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT), null);
+ static void dragIconToWorkspace(
+ LauncherInstrumentation launcher, Launchable launchable, Supplier<Point> destSupplier) {
+ dragIconToWorkspace(
+ launcher,
+ launchable,
+ destSupplier,
+ () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
+ /* expectDropEvents= */ null);
}
static void dragIconToWorkspace(
- LauncherInstrumentation launcher, Launchable launchable, Supplier<Point> dest,
- String longPressIndicator, Runnable expectLongClickEvents,
+ LauncherInstrumentation launcher,
+ Launchable launchable,
+ Supplier<Point> dest,
+ Runnable expectLongClickEvents,
@Nullable Runnable expectDropEvents) {
try (LauncherInstrumentation.Closable ignored = launcher.addContextLayer(
"want to drag icon to workspace")) {
final long downTime = SystemClock.uptimeMillis();
Point dragStart = launchable.startDrag(
downTime,
- longPressIndicator,
expectLongClickEvents,
/* runToSpringLoadedState= */ true);
Point targetDest = dest.get();
diff --git a/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java b/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java
index 93c2213..d8d4420 100644
--- a/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java
+++ b/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java
@@ -41,7 +41,6 @@
? launchableCenter.x - width / 2
: launchableCenter.x + width / 2,
displaySize.y / 2),
- launchable.getLongPressIndicator(),
startsActivity,
isWidgetShortcut,
launchable::addExpectedEventsForLongClick);