Merge "Properly marking TaplTestsSplitscreen with test annotations" into udc-qpr-dev
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 9fe0c00..0b83a88 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -306,6 +306,9 @@
// Initialize controllers after all are constructed.
mControllers.init(sharedState);
+ // This may not be necessary and can be reverted once we move towards recreating all
+ // controllers without re-creating the window
+ mControllers.rotationButtonController.onNavigationModeChanged(mNavMode.resValue);
updateSysuiStateFlags(sharedState.sysuiStateFlags, true /* fromInit */);
disableNavBarElements(sharedState.disableNavBarDisplayId, sharedState.disableNavBarState1,
sharedState.disableNavBarState2, false /* animate */);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index a935bac..c51a7ec 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.taskbar
+import android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR
import android.graphics.Insets
import android.graphics.Region
import android.os.Binder
@@ -43,6 +44,7 @@
import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
import com.android.launcher3.util.DisplayController
import java.io.PrintWriter
+import kotlin.jvm.optionals.getOrNull
/** Handles the insets that Taskbar provides to underlying apps and the IME. */
class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTaskbarController {
@@ -198,16 +200,22 @@
val imeInsetsSize = getInsetsForGravity(taskbarHeightForIme, gravity)
val imeInsetsSizeOverride =
- arrayOf(
- InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize),
- )
+ if (!ENABLE_HIDE_IME_CAPTION_BAR) {
+ arrayOf(
+ InsetsFrameProvider.InsetsSizeOverride(
+ TYPE_INPUT_METHOD,
+ imeInsetsSize
+ ),
+ )
+ } else {
+ arrayOf()
+ }
// Use 0 tappableElement insets for the VoiceInteractionWindow when gesture nav is enabled.
val visInsetsSizeForTappableElement =
if (context.isGestureNav) getInsetsForGravity(0, gravity)
else getInsetsForGravity(tappableHeight, gravity)
val insetsSizeOverrideForTappableElement =
- arrayOf(
- InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize),
+ imeInsetsSizeOverride + arrayOf(
InsetsFrameProvider.InsetsSizeOverride(
TYPE_VOICE_INTERACTION,
visInsetsSizeForTappableElement
@@ -216,7 +224,7 @@
if ((context.isGestureNav || TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
&& provider.type == tappableElement()) {
provider.insetsSizeOverrides = insetsSizeOverrideForTappableElement
- } else if (provider.type != systemGestures()) {
+ } else if (provider.type != systemGestures() && imeInsetsSizeOverride.isNotEmpty()) {
// We only override insets at the bottom of the screen
provider.insetsSizeOverrides = imeInsetsSizeOverride
}
@@ -283,9 +291,24 @@
controllers.uiController.isInOverview &&
DisplayController.isTransientTaskbar(context)
) {
- insetsInfo.touchableRegion.set(
+ val region =
controllers.taskbarActivityContext.dragLayer.lastDrawnTransientRect.toRegion()
- )
+ val bubbleBarBounds =
+ controllers.bubbleControllers.getOrNull()?.let { bubbleControllers ->
+ if (!bubbleControllers.bubbleStashController.isBubblesShowingOnOverview) {
+ return@let null
+ }
+ if (!bubbleControllers.bubbleBarViewController.isBubbleBarVisible) {
+ return@let null
+ }
+ bubbleControllers.bubbleBarViewController.bubbleBarBounds
+ }
+
+ // Include the bounds of the bubble bar in the touchable region if they exist.
+ if (bubbleBarBounds != null) {
+ region.op(bubbleBarBounds, Region.Op.UNION)
+ }
+ insetsInfo.touchableRegion.set(region)
} else {
insetsInfo.touchableRegion.set(touchableRegion)
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
index 00c2ca1..a5ea5a9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
@@ -197,6 +197,11 @@
}
}
+ /** Whether bubbles are showing on Overview. */
+ public boolean isBubblesShowingOnOverview() {
+ return mBubblesShowingOnOverview;
+ }
+
/** Called when sysui locked state changes, when locked, bubble bar is stashed. */
public void onSysuiLockedStateChange(boolean isSysuiLocked) {
if (isSysuiLocked != mIsSysuiLocked) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index d74a13b..b444b49 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -34,7 +34,6 @@
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE;
-import static com.android.launcher3.config.FeatureFlags.RECEIVE_UNFOLD_EVENTS_FROM_SYSUI;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition;
@@ -58,7 +57,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
-import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
@@ -68,8 +66,6 @@
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.hardware.SensorManager;
-import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.DisplayManager;
import android.media.permission.SafeCloseable;
import android.os.Build;
@@ -157,7 +153,6 @@
import com.android.quickstep.TouchInteractionService.TISBinder;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.LauncherUnfoldAnimationController;
-import com.android.quickstep.util.ProxyScreenStatusProvider;
import com.android.quickstep.util.QuickstepOnboardingPrefs;
import com.android.quickstep.util.SplitSelectStateController;
import com.android.quickstep.util.SplitToWorkspaceController;
@@ -171,14 +166,11 @@
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.unfold.RemoteUnfoldSharedComponent;
-import com.android.systemui.unfold.UnfoldSharedComponent;
import com.android.systemui.unfold.UnfoldTransitionFactory;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig;
import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.systemui.unfold.progress.RemoteUnfoldTransitionReceiver;
-import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider;
-import com.android.systemui.unfold.system.DeviceStateManagerFoldProvider;
import com.android.systemui.unfold.updates.RotationChangeProvider;
import java.io.FileDescriptor;
@@ -465,10 +457,7 @@
public void onDestroy() {
mAppTransitionManager.onActivityDestroyed();
if (mUnfoldTransitionProgressProvider != null) {
- if (FeatureFlags.RECEIVE_UNFOLD_EVENTS_FROM_SYSUI.get()) {
- SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null);
- }
-
+ SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null);
mUnfoldTransitionProgressProvider.destroy();
}
@@ -482,6 +471,10 @@
mDesktopVisibilityController.unregisterSystemUiListener();
}
+ if (mSplitSelectStateController != null) {
+ mSplitSelectStateController.onDestroy();
+ }
+
super.onDestroy();
mHotseatPredictionController.destroy();
mSplitWithKeyboardShortcutController.onDestroy();
@@ -905,43 +898,10 @@
private void initUnfoldTransitionProgressProvider() {
final UnfoldTransitionConfig config = new ResourceUnfoldTransitionConfig();
if (config.isEnabled()) {
- if (RECEIVE_UNFOLD_EVENTS_FROM_SYSUI.get()) {
- initRemotelyCalculatedUnfoldAnimation(config);
- } else {
- initLocallyCalculatedUnfoldAnimation(config);
- }
-
+ initRemotelyCalculatedUnfoldAnimation(config);
}
}
- /** Registers hinge angle listener and calculates the animation progress in this process. */
- private void initLocallyCalculatedUnfoldAnimation(UnfoldTransitionConfig config) {
- UnfoldSharedComponent unfoldComponent =
- UnfoldTransitionFactory.createUnfoldSharedComponent(
- /* context= */ this,
- config,
- ProxyScreenStatusProvider.INSTANCE,
- new DeviceStateManagerFoldProvider(
- getSystemService(DeviceStateManager.class), /* context= */ this),
- new ActivityManagerActivityTypeProvider(
- getSystemService(ActivityManager.class)),
- getSystemService(SensorManager.class),
- getMainThreadHandler(),
- getMainExecutor(),
- /* backgroundExecutor= */ UI_HELPER_EXECUTOR,
- /* tracingTagPrefix= */ "launcher",
- getSystemService(DisplayManager.class)
- );
-
- mUnfoldTransitionProgressProvider = unfoldComponent.getUnfoldTransitionProvider()
- .orElseThrow(() -> new IllegalStateException(
- "Trying to create UnfoldTransitionProgressProvider when the "
- + "transition is disabled"));
-
- initUnfoldAnimationController(mUnfoldTransitionProgressProvider,
- unfoldComponent.getRotationChangeProvider());
- }
-
/** Receives animation progress from sysui process. */
private void initRemotelyCalculatedUnfoldAnimation(UnfoldTransitionConfig config) {
RemoteUnfoldSharedComponent unfoldComponent =
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 8cb542c..1ef4039 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -518,20 +518,22 @@
// Set up a entire animation lifecycle callback to notify the current recents view when
// the animation is canceled
mGestureState.runOnceAtState(STATE_RECENTS_ANIMATION_CANCELED, () -> {
- HashMap<Integer, ThumbnailData> snapshots =
- mGestureState.consumeRecentsAnimationCanceledSnapshot();
- if (snapshots != null) {
- mRecentsView.switchToScreenshot(snapshots, () -> {
- if (mRecentsAnimationController != null) {
- mRecentsAnimationController.cleanupScreenshot();
- } else if (mDeferredCleanupRecentsAnimationController != null) {
- mDeferredCleanupRecentsAnimationController.cleanupScreenshot();
- mDeferredCleanupRecentsAnimationController = null;
- }
- });
- mRecentsView.onRecentsAnimationComplete();
- }
- });
+ if (mRecentsView == null) return;
+
+ HashMap<Integer, ThumbnailData> snapshots =
+ mGestureState.consumeRecentsAnimationCanceledSnapshot();
+ if (snapshots != null) {
+ mRecentsView.switchToScreenshot(snapshots, () -> {
+ if (mRecentsAnimationController != null) {
+ mRecentsAnimationController.cleanupScreenshot();
+ } else if (mDeferredCleanupRecentsAnimationController != null) {
+ mDeferredCleanupRecentsAnimationController.cleanupScreenshot();
+ mDeferredCleanupRecentsAnimationController = null;
+ }
+ });
+ mRecentsView.onRecentsAnimationComplete();
+ }
+ });
setupRecentsViewUi();
mRecentsView.runOnPageScrollsInitialized(this::linkRecentsViewScroll);
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 2e1a62c..72439de 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -37,6 +37,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Trace;
+import android.util.Log;
import android.view.Display;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
@@ -59,7 +60,6 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
@@ -67,6 +67,7 @@
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.FallbackTaskbarUIController;
import com.android.launcher3.taskbar.TaskbarManager;
+import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.ActivityOptionsWrapper;
import com.android.launcher3.util.ActivityTracker;
import com.android.launcher3.util.RunnableList;
@@ -393,7 +394,7 @@
super.onDestroy();
ACTIVITY_TRACKER.onActivityDestroyed(this);
mActivityLaunchAnimationRunner = null;
-
+ mSplitSelectStateController.onDestroy();
mTISBindHelper.onDestroy();
}
@@ -404,6 +405,7 @@
}
public void startHome() {
+ Log.d(TestProtocol.INCORRECT_HOME_STATE, "start home from recents activity");
RecentsView recentsView = getOverviewPanel();
recentsView.switchToScreenshot(() -> recentsView.finishRecentsAnimation(true,
this::startHomeInternal));
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 6dbb5bf..0b5a070 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -38,6 +38,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
import com.android.quickstep.util.ActiveGestureLog;
@@ -179,6 +180,9 @@
RecentsView recentsView =
activityInterface.getCreatedActivity().getOverviewPanel();
if (recentsView != null) {
+ Log.d(TestProtocol.INCORRECT_HOME_STATE,
+ "finish recents animation on "
+ + compat.taskInfo.description);
recentsView.finishRecentsAnimation(true, null);
}
return;
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 79c7329..22aca25 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -70,6 +70,7 @@
import android.os.SystemClock;
import android.util.Log;
import android.view.Choreographer;
+import android.view.InputDevice;
import android.view.InputEvent;
import android.view.MotionEvent;
import android.view.SurfaceControl;
@@ -116,7 +117,6 @@
import com.android.quickstep.inputconsumers.TrackpadStatusBarInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActiveGestureLog.CompoundString;
-import com.android.quickstep.util.ProxyScreenStatusProvider;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -302,24 +302,6 @@
@BinderThread
@Override
- public void onScreenTurnedOn() {
- MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurnedOn);
- }
-
- @BinderThread
- @Override
- public void onScreenTurningOn() {
- MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurningOn);
- }
-
- @BinderThread
- @Override
- public void onScreenTurningOff() {
- MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurningOff);
- }
-
- @BinderThread
- @Override
public void enterStageSplitFromRunningApp(boolean leftOrTop) {
executeForTouchInteractionService(tis -> {
StatefulActivity activity =
@@ -771,7 +753,7 @@
if (mGestureState.isTrackpadGesture() && (action == ACTION_POINTER_DOWN
|| action == ACTION_POINTER_UP)) {
// Skip ACTION_POINTER_DOWN and ACTION_POINTER_UP events from trackpad.
- } else if (event.isHoverEvent()) {
+ } else if (isCursorHoverEvent(event)) {
mUncheckedConsumer.onHoverEvent(event);
} else {
mUncheckedConsumer.onMotionEvent(event);
@@ -783,6 +765,11 @@
traceToken.close();
}
+ // Talkback generates hover events on touch, which we do not want to consume.
+ private boolean isCursorHoverEvent(MotionEvent event) {
+ return event.isHoverEvent() && event.getSource() == InputDevice.SOURCE_MOUSE;
+ }
+
private InputConsumer tryCreateAssistantInputConsumer(
GestureState gestureState, MotionEvent motionEvent) {
return tryCreateAssistantInputConsumer(
diff --git a/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java b/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java
deleted file mode 100644
index 8f79ccf..0000000
--- a/quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2021 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.util;
-
-import androidx.annotation.NonNull;
-
-import com.android.systemui.unfold.updates.screen.ScreenStatusProvider;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Screen status provider implementation that exposes methods to provide screen
- * status updates to listeners. It is used to receive screen turned on event from
- * SystemUI to Launcher.
- */
-public class ProxyScreenStatusProvider implements ScreenStatusProvider {
-
- public static final ProxyScreenStatusProvider INSTANCE = new ProxyScreenStatusProvider();
- private final List<ScreenListener> mListeners = new ArrayList<>();
-
- /**
- * Called when the screen is on and ready (windows are drawn and screen blocker is removed)
- */
- public void onScreenTurnedOn() {
- mListeners.forEach(ScreenListener::onScreenTurnedOn);
- }
-
- /** Called when the screen is starting to turn on. */
- public void onScreenTurningOn() {
- mListeners.forEach(ScreenListener::onScreenTurningOn);
- }
-
- /** Called when the screen is starting to turn off. */
- public void onScreenTurningOff() {
- mListeners.forEach(ScreenListener::onScreenTurningOff);
- }
-
- @Override
- public void addCallback(@NonNull ScreenListener listener) {
- mListeners.add(listener);
- }
-
- @Override
- public void removeCallback(@NonNull ScreenListener listener) {
- mListeners.remove(listener);
- }
-}
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 0c89766..6d5aa16 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -113,7 +113,7 @@
public class SplitSelectStateController {
private static final String TAG = "SplitSelectStateCtor";
- private final Context mContext;
+ private Context mContext;
private final Handler mHandler;
private final RecentsModel mRecentTasksModel;
private final SplitAnimationController mSplitAnimationController;
@@ -157,6 +157,10 @@
mSplitSelectDataHolder = new SplitSelectDataHolder(mContext);
}
+ public void onDestroy() {
+ mContext = null;
+ }
+
/**
* @param alreadyRunningTask if set to {@link android.app.ActivityTaskManager#INVALID_TASK_ID}
* then @param intent will be used to launch the initial task
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 4b8741d..cb5b457 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -5296,6 +5296,8 @@
cleanupRemoteTargets();
if (mRecentsAnimationController == null) {
+ Log.d(TestProtocol.INCORRECT_HOME_STATE, "finish recents animation but recents "
+ + "animation controller was null. returning.");
if (onFinishComplete != null) {
onFinishComplete.run();
}
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 8876a1b..808cf70 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -17,12 +17,7 @@
package com.android.launcher3;
import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
-import android.app.WallpaperColors;
-import android.app.WallpaperManager;
-import android.app.WallpaperManager.OnColorsChangedListener;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Point;
@@ -32,6 +27,7 @@
import android.view.Display;
import android.view.View;
+import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -41,9 +37,11 @@
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
import com.android.launcher3.util.DisplayController.Info;
+import com.android.launcher3.util.OnColorHintListener;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.util.WallpaperColorHints;
import com.android.launcher3.util.WindowBounds;
/**
@@ -51,7 +49,7 @@
*/
@SuppressWarnings("NewApi")
public abstract class BaseDraggingActivity extends BaseActivity
- implements OnColorsChangedListener, DisplayInfoChangeListener {
+ implements OnColorHintListener, DisplayInfoChangeListener {
private static final String TAG = "BaseDraggingActivity";
@@ -63,8 +61,7 @@
protected boolean mIsSafeModeEnabled;
private Runnable mOnStartCallback;
- private RunnableList mOnResumeCallbacks = new RunnableList();
-
+ private final RunnableList mOnResumeCallbacks = new RunnableList();
private int mThemeRes = R.style.AppTheme;
@Override
@@ -76,10 +73,7 @@
DisplayController.INSTANCE.get(this).addChangeListener(this);
// Update theme
- if (Utilities.ATLEAST_P) {
- THREAD_POOL_EXECUTOR.execute(() -> getSystemService(WallpaperManager.class)
- .addOnColorsChangedListener(this, MAIN_EXECUTOR.getHandler()));
- }
+ WallpaperColorHints.get(this).registerOnColorHintsChangedListener(this);
int themeRes = Themes.getActivityThemeRes(this);
if (themeRes != mThemeRes) {
mThemeRes = themeRes;
@@ -97,8 +91,9 @@
mOnResumeCallbacks.add(callback);
}
+ @MainThread
@Override
- public void onColorsChanged(WallpaperColors wallpaperColors, int which) {
+ public void onColorHintsChanged(int colorHints) {
updateTheme();
}
@@ -175,10 +170,8 @@
@Override
protected void onDestroy() {
super.onDestroy();
- if (Utilities.ATLEAST_P) {
- getSystemService(WallpaperManager.class).removeOnColorsChangedListener(this);
- }
DisplayController.INSTANCE.get(this).removeChangeListener(this);
+ WallpaperColorHints.get(this).unregisterOnColorsChangedListener(this);
}
public void runOnceOnStart(Runnable action) {
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 360e060..abf84dd 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -16,6 +16,7 @@
package com.android.launcher3;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES;
import static com.android.launcher3.config.FeatureFlags.ENABLE_DOWNLOAD_APP_UX_V2;
import static com.android.launcher3.config.FeatureFlags.ENABLE_ICON_LABEL_AUTO_SCALING;
import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
@@ -152,7 +153,7 @@
private final CheckLongPressHelper mLongPressHelper;
- private final boolean mLayoutHorizontal;
+ private boolean mLayoutHorizontal;
private final boolean mIsRtl;
private final int mIconSize;
@@ -197,6 +198,7 @@
public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mActivity = ActivityContext.lookupContext(context);
+ FastBitmapDrawable.setFlagHoverEnabled(ENABLE_CURSOR_HOVER_STATES.get());
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.BubbleTextView, defStyle, 0);
@@ -666,6 +668,18 @@
}
/**
+ * Sets whether the layout is horizontal.
+ */
+ public void setLayoutHorizontal(boolean layoutHorizontal) {
+ if (mLayoutHorizontal == layoutHorizontal) {
+ return;
+ }
+
+ mLayoutHorizontal = layoutHorizontal;
+ applyCompoundDrawables(getIconOrTransparentColor());
+ }
+
+ /**
* Sets whether to vertically center the content.
*/
public void setCenterVertically(boolean centerVertically) {
@@ -991,10 +1005,14 @@
if (!mIsIconVisible) {
resetIconScale();
}
- Drawable icon = visible ? mIcon : new ColorDrawable(Color.TRANSPARENT);
+ Drawable icon = getIconOrTransparentColor();
applyCompoundDrawables(icon);
}
+ private Drawable getIconOrTransparentColor() {
+ return mIsIconVisible ? mIcon : new ColorDrawable(Color.TRANSPARENT);
+ }
+
/** Sets the icon visual state to disabled or not. */
public void setIconDisabled(boolean isDisabled) {
if (mIcon != null) {
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 4674401..08e5def 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -245,6 +245,7 @@
// the user where a dragged item will land when dropped.
setWillNotDraw(false);
setClipToPadding(false);
+ setClipChildren(false);
mActivity = ActivityContext.lookupContext(context);
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 7ece9a4..a48c928 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -1134,10 +1134,11 @@
* This method calculates the space between the icons to achieve a certain width.
*/
private int calculateHotseatBorderSpace(float hotseatWidthPx, int numExtraBorder) {
+ int numBorders = (numShownHotseatIcons - 1 + numExtraBorder);
+ if (numBorders <= 0) return 0;
+
float hotseatIconsTotalPx = iconSizePx * numShownHotseatIcons;
- int hotseatBorderSpacePx =
- (int) (hotseatWidthPx - hotseatIconsTotalPx)
- / (numShownHotseatIcons - 1 + numExtraBorder);
+ int hotseatBorderSpacePx = (int) (hotseatWidthPx - hotseatIconsTotalPx) / numBorders;
return Math.min(hotseatBorderSpacePx, mMaxHotseatIconSpacePx);
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4e7a884..ffb8b82 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -198,6 +198,7 @@
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.ActivityResultInfo;
import com.android.launcher3.util.ActivityTracker;
+import com.android.launcher3.util.CannedAnimationCoordinator;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
@@ -421,6 +422,9 @@
private StartupLatencyLogger mStartupLatencyLogger;
private CellPosMapper mCellPosMapper = CellPosMapper.DEFAULT;
+ private final CannedAnimationCoordinator mAnimationCoordinator =
+ new CannedAnimationCoordinator(this);
+
@Override
@TargetApi(Build.VERSION_CODES.S)
protected void onCreate(Bundle savedInstanceState) {
@@ -2343,13 +2347,37 @@
mWorkspace.unlockWallpaperFromDefaultPageOnNextLayout();
}
+ /**
+ * Remove odd number because they are already included when isTwoPanels and add the pair screen
+ * if not present.
+ */
+ private IntArray filterTwoPanelScreenIds(IntArray orderedScreenIds) {
+ IntSet screenIds = IntSet.wrap(orderedScreenIds);
+ orderedScreenIds.forEach(screenId -> {
+ if (screenId % 2 == 1) {
+ screenIds.remove(screenId);
+ // In case the pair is not added, add it
+ if (!mWorkspace.containsScreenId(screenId - 1)) {
+ screenIds.add(screenId - 1);
+ }
+ }
+ });
+ return screenIds.getArray();
+ }
+
private void bindAddScreens(IntArray orderedScreenIds) {
+
if (mDeviceProfile.isTwoPanels) {
- // Some empty pages might have been removed while the phone was in a single panel
- // mode, so we want to add those empty pages back.
- IntSet screenIds = IntSet.wrap(orderedScreenIds);
- orderedScreenIds.forEach(screenId -> screenIds.add(mWorkspace.getScreenPair(screenId)));
- orderedScreenIds = screenIds.getArray();
+ if (FOLDABLE_SINGLE_PAGE.get()) {
+ orderedScreenIds = filterTwoPanelScreenIds(orderedScreenIds);
+ } else {
+ // Some empty pages might have been removed while the phone was in a single panel
+ // mode, so we want to add those empty pages back.
+ IntSet screenIds = IntSet.wrap(orderedScreenIds);
+ orderedScreenIds.forEach(
+ screenId -> screenIds.add(mWorkspace.getScreenPair(screenId)));
+ orderedScreenIds = screenIds.getArray();
+ }
}
int count = orderedScreenIds.size();
@@ -3398,4 +3426,11 @@
public void launchAppPair(WorkspaceItemInfo app1, WorkspaceItemInfo app2) {
// Overridden
}
+
+ /**
+ * Returns the animation coordinator for playing one-off animations
+ */
+ public CannedAnimationCoordinator getAnimationCoordinator() {
+ return mAnimationCoordinator;
+ }
}
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 07b71b3..f0fea61 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -66,6 +66,7 @@
mActivity = ActivityContext.lookupContext(context);
mWallpaperManager = WallpaperManager.getInstance(context);
mContainerType = containerType;
+ setClipChildren(false);
}
public void setCellDimensions(int cellWidth, int cellHeight, int countX, int countY,
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index adaf20f..8be8fed 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -731,6 +731,14 @@
});
}
+
+ /**
+ * Returns if the given screenId is already in the Workspace
+ */
+ public boolean containsScreenId(int screenId) {
+ return this.mWorkspaceScreens.containsKey(screenId);
+ }
+
/**
* Inserts extra empty pages to the end of the existing workspaces.
* Usually we add one extra empty screen, but when two panel home is enabled we add
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index b836491..9bc2a0a 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -297,11 +297,6 @@
"ENABLE_APP_ICON_IN_INLINE_SHORTCUTS", DISABLED, "Show app icon for inline shortcut");
// TODO(Block 22): Clean up flags
- public static final BooleanFlag RECEIVE_UNFOLD_EVENTS_FROM_SYSUI = getDebugFlag(270397209,
- "RECEIVE_UNFOLD_EVENTS_FROM_SYSUI", ENABLED,
- "Enables receiving unfold animation events from sysui instead of calculating "
- + "them in launcher process using hinge sensor values.");
-
public static final BooleanFlag ENABLE_WIDGET_TRANSITION_FOR_RESIZING = getDebugFlag(268553314,
"ENABLE_WIDGET_TRANSITION_FOR_RESIZING", DISABLED,
"Enable widget transition animation when resizing the widgets");
@@ -414,12 +409,12 @@
// TODO(Block 33): Clean up flags
public static final BooleanFlag ENABLE_ALL_APPS_RV_PREINFLATION = getDebugFlag(288161355,
- "ENABLE_ALL_APPS_RV_PREINFLATION", DISABLED,
+ "ENABLE_ALL_APPS_RV_PREINFLATION", ENABLED,
"Enables preinflating all apps icons to avoid scrolling jank.");
// TODO(Block 34): Clean up flags
public static final BooleanFlag ALL_APPS_GONE_VISIBILITY = getDebugFlag(291651514,
- "ALL_APPS_GONE_VISIBILITY", DISABLED,
+ "ALL_APPS_GONE_VISIBILITY", ENABLED,
"Set all apps container view's hidden visibility to GONE instead of INVISIBLE.");
// TODO(Block 35): Empty block
diff --git a/src/com/android/launcher3/util/CannedAnimationCoordinator.kt b/src/com/android/launcher3/util/CannedAnimationCoordinator.kt
new file mode 100644
index 0000000..18f8339
--- /dev/null
+++ b/src/com/android/launcher3/util/CannedAnimationCoordinator.kt
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2023 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.util
+
+import android.animation.AnimatorSet
+import android.animation.ValueAnimator
+import android.util.Log
+import android.view.ViewTreeObserver.OnGlobalLayoutListener
+import androidx.core.view.OneShotPreDrawListener
+import com.android.app.animation.Interpolators.LINEAR
+import com.android.launcher3.anim.AnimatorListeners
+import com.android.launcher3.anim.AnimatorPlaybackController
+import com.android.launcher3.anim.PendingAnimation
+import com.android.launcher3.statemanager.StatefulActivity
+import java.util.function.Consumer
+
+private const val TAG = "CannedAnimCoordinator"
+
+/**
+ * Utility class to run a canned animation on Launcher.
+ *
+ * This class takes care to registering animations with stateManager and ensures that only one
+ * animation is playing at a time.
+ */
+class CannedAnimationCoordinator(private val activity: StatefulActivity<*>) {
+
+ private val launcherLayoutListener = OnGlobalLayoutListener { scheduleRecreateAnimOnPreDraw() }
+ private var recreatePending = false
+
+ private var animationProvider: Any? = null
+
+ private var animationDuration: Long = 0L
+ private var animationFactory: Consumer<PendingAnimation>? = null
+ private var animationController: AnimatorPlaybackController? = null
+
+ private var currentAnim: AnimatorPlaybackController? = null
+
+ /**
+ * Sets the current animation cancelling any previously set animation.
+ *
+ * Callers can control the animation using {@link #getPlaybackController}. The state is
+ * automatically cleared when the playback controller ends. The animation is automatically
+ * recreated when any layout change happens. Callers can also ask for recreation by calling
+ * {@link #recreateAnimation}
+ */
+ fun setAnimation(provider: Any, factory: Consumer<PendingAnimation>, duration: Long) {
+ if (provider != animationProvider) {
+ Log.e(TAG, "Trying to play two animations together, $provider and $animationProvider")
+ }
+
+ // Cancel any previously running animation
+ endCurrentAnimation(false)
+ animationController?.dispatchOnCancel()?.dispatchOnEnd()
+
+ animationProvider = provider
+ animationFactory = factory
+ animationDuration = duration
+
+ // Setup a new controller and link it with launcher state animation
+ val anim = AnimatorSet()
+ anim.play(
+ ValueAnimator.ofFloat(0f, 1f).apply {
+ interpolator = LINEAR
+ this.duration = duration
+ addUpdateListener { anim -> currentAnim?.setPlayFraction(anim.animatedFraction) }
+ }
+ )
+ val controller = AnimatorPlaybackController.wrap(anim, duration)
+ anim.addListener(
+ AnimatorListeners.forEndCallback { success ->
+ if (animationController != controller) {
+ return@forEndCallback
+ }
+
+ endCurrentAnimation(success)
+ animationController = null
+ animationFactory = null
+ animationProvider = null
+
+ activity.rootView.viewTreeObserver.apply {
+ if (isAlive) {
+ removeOnGlobalLayoutListener(launcherLayoutListener)
+ }
+ }
+ }
+ )
+
+ // Recreate animation whenever layout happens in case transforms change during layout
+ activity.rootView.viewTreeObserver.apply {
+ if (isAlive) {
+ addOnGlobalLayoutListener(launcherLayoutListener)
+ }
+ }
+ // Link this to the state manager so that it auto-cancels when state changes
+ recreatePending = false
+ animationController =
+ controller.apply { activity.stateManager.setCurrentUserControlledAnimation(this) }
+ recreateAnimation(provider)
+ }
+
+ private fun endCurrentAnimation(success: Boolean) {
+ currentAnim?.apply {
+ // When cancelling an animation, apply final progress so that all transformations
+ // are restored
+ setPlayFraction(1f)
+ if (!success) dispatchOnCancel()
+ dispatchOnEnd()
+ }
+ currentAnim = null
+ }
+
+ /** Returns the current animation controller to control the animation */
+ fun getPlaybackController(provider: Any): AnimatorPlaybackController? {
+ return if (provider == animationProvider) animationController
+ else {
+ Log.d(TAG, "Wrong controller access from $provider, actual provider $animationProvider")
+ null
+ }
+ }
+
+ private fun scheduleRecreateAnimOnPreDraw() {
+ if (!recreatePending) {
+ recreatePending = true
+ OneShotPreDrawListener.add(activity.rootView) {
+ if (recreatePending) {
+ recreatePending = false
+ animationProvider?.apply { recreateAnimation(this) }
+ }
+ }
+ }
+ }
+
+ /** Notify the controller to recreate the animation. The animation progress is preserved */
+ fun recreateAnimation(provider: Any) {
+ if (provider != animationProvider) {
+ Log.e(TAG, "Ignore recreate request from $provider, actual provider $animationProvider")
+ return
+ }
+ endCurrentAnimation(false /* success */)
+
+ if (animationFactory == null || animationController == null) {
+ return
+ }
+ currentAnim =
+ PendingAnimation(animationDuration)
+ .apply { animationFactory?.accept(this) }
+ .createPlaybackController()
+ .apply { setPlayFraction(animationController!!.progressFraction) }
+ }
+}
diff --git a/src/com/android/launcher3/util/MultiScalePropertyFactory.java b/src/com/android/launcher3/util/MultiScalePropertyFactory.java
index a7e6cc8..cf8d6cc 100644
--- a/src/com/android/launcher3/util/MultiScalePropertyFactory.java
+++ b/src/com/android/launcher3/util/MultiScalePropertyFactory.java
@@ -40,8 +40,7 @@
private static final boolean DEBUG = false;
private static final String TAG = "MultiScaleProperty";
private final String mName;
- private final ArrayMap<Integer, MultiScaleProperty> mProperties =
- new ArrayMap<Integer, MultiScaleProperty>();
+ private final ArrayMap<Integer, MultiScaleProperty> mProperties = new ArrayMap<>();
// This is an optimization for cases when set is called repeatedly with the same setterIndex.
private float mMinOfOthers = 0;
@@ -55,7 +54,7 @@
}
/** Returns the [MultiFloatProperty] associated with [inx], creating it if not present. */
- public MultiScaleProperty get(Integer index) {
+ public FloatProperty<T> get(Integer index) {
return mProperties.computeIfAbsent(index,
(k) -> new MultiScaleProperty(index, mName + "_" + index));
}
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index a5c663f..60951ba 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -21,8 +21,6 @@
import static com.android.launcher3.LauncherPrefs.THEMED_ICONS;
-import android.app.WallpaperColors;
-import android.app.WallpaperManager;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
@@ -48,16 +46,9 @@
public static final String KEY_THEMED_ICONS = "themed_icons";
+ /** Gets the WallpaperColorHints and then uses those to get the correct activity theme res. */
public static int getActivityThemeRes(Context context) {
- final int colorHints;
- if (Utilities.ATLEAST_P) {
- WallpaperColors colors = context.getSystemService(WallpaperManager.class)
- .getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
- colorHints = colors == null ? 0 : colors.getColorHints();
- } else {
- colorHints = 0;
- }
- return getActivityThemeRes(context, colorHints);
+ return getActivityThemeRes(context, WallpaperColorHints.get(context).getHints());
}
public static int getActivityThemeRes(Context context, int wallpaperColorHints) {
diff --git a/src/com/android/launcher3/util/WallpaperColorHints.kt b/src/com/android/launcher3/util/WallpaperColorHints.kt
new file mode 100644
index 0000000..1361c1e
--- /dev/null
+++ b/src/com/android/launcher3/util/WallpaperColorHints.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2023 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.util
+
+import android.app.WallpaperColors
+import android.app.WallpaperManager
+import android.app.WallpaperManager.FLAG_SYSTEM
+import android.app.WallpaperManager.OnColorsChangedListener
+import android.content.Context
+import androidx.annotation.MainThread
+import androidx.annotation.VisibleForTesting
+import com.android.launcher3.Utilities
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
+
+/**
+ * This class caches the system's wallpaper color hints for use by other classes as a performance
+ * enhancer. It also centralizes all the WallpaperManager color hint code in one location.
+ */
+class WallpaperColorHints(private val context: Context) : SafeCloseable {
+ var hints: Int = 0
+ private set
+ private val wallpaperManager
+ get() = context.getSystemService(WallpaperManager::class.java)!!
+ private val onColorHintsChangedListeners = mutableListOf<OnColorHintListener>()
+ private val onClose: SafeCloseable
+
+ init {
+ if (Utilities.ATLEAST_S) {
+ hints = wallpaperManager.getWallpaperColors(FLAG_SYSTEM)?.colorHints ?: 0
+ val onColorsChangedListener = OnColorsChangedListener { colors, which ->
+ onColorsChanged(colors, which)
+ }
+ UI_HELPER_EXECUTOR.execute {
+ wallpaperManager.addOnColorsChangedListener(
+ onColorsChangedListener,
+ MAIN_EXECUTOR.handler
+ )
+ }
+ onClose = SafeCloseable {
+ UI_HELPER_EXECUTOR.execute {
+ wallpaperManager.removeOnColorsChangedListener(onColorsChangedListener)
+ }
+ }
+ } else {
+ onClose = SafeCloseable {}
+ }
+ }
+
+ @MainThread
+ private fun onColorsChanged(colors: WallpaperColors?, which: Int) {
+ if ((which and FLAG_SYSTEM) != 0 && Utilities.ATLEAST_S) {
+ val newHints = colors?.colorHints ?: 0
+ if (newHints != hints) {
+ hints = newHints
+ onColorHintsChangedListeners.forEach { it.onColorHintsChanged(newHints) }
+ }
+ }
+ }
+
+ override fun close() = onClose.close()
+
+ fun registerOnColorHintsChangedListener(listener: OnColorHintListener) {
+ onColorHintsChangedListeners.add(listener)
+ }
+
+ fun unregisterOnColorsChangedListener(listener: OnColorHintListener) {
+ onColorHintsChangedListeners.remove(listener)
+ }
+
+ companion object {
+ @VisibleForTesting
+ @JvmField
+ val INSTANCE = MainThreadInitializedObject { WallpaperColorHints(it) }
+ @JvmStatic fun get(context: Context): WallpaperColorHints = INSTANCE.get(context)
+ }
+}
+
+interface OnColorHintListener {
+ fun onColorHintsChanged(colorHints: Int)
+}
diff --git a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index bbe4c20..87ec260 100644
--- a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -161,7 +161,7 @@
public static final String LAUNCH_SPLIT_PAIR = "b/288939273";
public static final String OVERVIEW_OVER_HOME = "b/279059025";
-
+ public static final String INCORRECT_HOME_STATE = "b/293191790";
public static final String REQUEST_EMULATE_DISPLAY = "emulate-display";
public static final String REQUEST_STOP_EMULATE_DISPLAY = "stop-emulate-display";
public static final String REQUEST_IS_EMULATE_DISPLAY_RUNNING = "is-emulate-display-running";
diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutTestCaseReader.java b/tests/src/com/android/launcher3/celllayout/CellLayoutTestCaseReader.java
index e33a304..419cb3d 100644
--- a/tests/src/com/android/launcher3/celllayout/CellLayoutTestCaseReader.java
+++ b/tests/src/com/android/launcher3/celllayout/CellLayoutTestCaseReader.java
@@ -63,8 +63,8 @@
}
public static class Board extends TestSection {
- Point gridSize;
- String board;
+ public Point gridSize;
+ public String board;
public Board(Point gridSize, String board) {
super(State.BOARD);
@@ -127,7 +127,7 @@
}
}
- List<TestSection> parse() {
+ public List<TestSection> parse() {
List<TestSection> sections = new ArrayList<>();
String[] lines = mTest.split("\n");
Iterator<String> it = Arrays.stream(lines).iterator();
diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java b/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java
index b6c55af..86a7bd3 100644
--- a/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java
+++ b/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java
@@ -42,7 +42,7 @@
params.getCellX(), params.getCellY(),
launcher.getWorkspace().getIdForScreen(cellLayout), CONTAINER_DESKTOP);
int screenId = pos.screenId;
- if (screenId > boards.size() - 1) {
+ for (int j = boards.size(); j <= screenId; j++) {
boards.add(new CellLayoutBoard(cellLayout.getCountX(), cellLayout.getCountY()));
}
CellLayoutBoard board = boards.get(screenId);
diff --git a/tests/src/com/android/launcher3/icons/FastBitmapDrawableTest.java b/tests/src/com/android/launcher3/icons/FastBitmapDrawableTest.java
new file mode 100644
index 0000000..038c98b
--- /dev/null
+++ b/tests/src/com/android/launcher3/icons/FastBitmapDrawableTest.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2023 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.icons;
+
+import static com.android.launcher3.icons.FastBitmapDrawable.CLICK_FEEDBACK_DURATION;
+import static com.android.launcher3.icons.FastBitmapDrawable.HOVERED_SCALE;
+import static com.android.launcher3.icons.FastBitmapDrawable.HOVER_FEEDBACK_DURATION;
+import static com.android.launcher3.icons.FastBitmapDrawable.PRESSED_SCALE;
+import static com.android.launcher3.icons.FastBitmapDrawable.SCALE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.graphics.Bitmap;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.PathInterpolator;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Spy;
+
+/**
+ * Tests for FastBitmapDrawable.
+ */
+@SmallTest
+@UiThreadTest
+@RunWith(AndroidJUnit4.class)
+public class FastBitmapDrawableTest {
+ private static final float EPSILON = 0.00001f;
+
+ @Spy
+ FastBitmapDrawable mFastBitmapDrawable =
+ spy(new FastBitmapDrawable(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)));
+
+ @Before
+ public void setUp() {
+ FastBitmapDrawable.setFlagHoverEnabled(true);
+ when(mFastBitmapDrawable.isVisible()).thenReturn(true);
+ mFastBitmapDrawable.mIsPressed = false;
+ mFastBitmapDrawable.mIsHovered = false;
+ mFastBitmapDrawable.resetScale();
+ }
+
+ @Test
+ public void testOnStateChange_noState() {
+ int[] state = new int[]{};
+
+ boolean isHandled = mFastBitmapDrawable.onStateChange(state);
+
+ // No scale changes without state change.
+ assertFalse("State change handled.", isHandled);
+ assertNull("Scale animation not null.", mFastBitmapDrawable.mScaleAnimation);
+ }
+
+ @Test
+ public void testOnStateChange_statePressed() {
+ int[] state = new int[]{android.R.attr.state_pressed};
+
+ boolean isHandled = mFastBitmapDrawable.onStateChange(state);
+
+ // Animate to state pressed.
+ assertTrue("State change not handled.", isHandled);
+ assertEquals("Duration not correct.", mFastBitmapDrawable.mScaleAnimation.getDuration(),
+ CLICK_FEEDBACK_DURATION);
+ mFastBitmapDrawable.mScaleAnimation.end();
+ assertEquals("End value not correct.",
+ (float) SCALE.get(mFastBitmapDrawable), PRESSED_SCALE, EPSILON);
+ assertTrue("Wrong interpolator used.",
+ mFastBitmapDrawable.mScaleAnimation.getInterpolator()
+ instanceof AccelerateInterpolator);
+ }
+
+ @Test
+ public void testOnStateChange_stateHovered() {
+ int[] state = new int[]{android.R.attr.state_hovered};
+
+ boolean isHandled = mFastBitmapDrawable.onStateChange(state);
+
+ // Animate to state hovered.
+ assertTrue("State change not handled.", isHandled);
+ assertEquals("Duration not correct.", mFastBitmapDrawable.mScaleAnimation.getDuration(),
+ HOVER_FEEDBACK_DURATION);
+ mFastBitmapDrawable.mScaleAnimation.end();
+ assertEquals("End value not correct.",
+ (float) SCALE.get(mFastBitmapDrawable), HOVERED_SCALE, EPSILON);
+ assertTrue("Wrong interpolator used.",
+ mFastBitmapDrawable.mScaleAnimation.getInterpolator() instanceof PathInterpolator);
+ }
+
+ @Test
+ public void testOnStateChange_stateHoveredFlagDisabled() {
+ FastBitmapDrawable.setFlagHoverEnabled(false);
+ int[] state = new int[]{android.R.attr.state_hovered};
+
+ boolean isHandled = mFastBitmapDrawable.onStateChange(state);
+
+ // No state change with flag disabled.
+ assertFalse("Hover state change handled with flag disabled.", isHandled);
+ assertNull("Animation should not run with hover flag disabled.",
+ mFastBitmapDrawable.mScaleAnimation);
+ }
+
+ @Test
+ public void testOnStateChange_statePressedAndHovered() {
+ int[] state = new int[]{android.R.attr.state_pressed, android.R.attr.state_hovered};
+
+ boolean isHandled = mFastBitmapDrawable.onStateChange(state);
+
+ // Animate to pressed state only.
+ assertTrue("State change not handled.", isHandled);
+ assertEquals("Duration not correct.", mFastBitmapDrawable.mScaleAnimation.getDuration(),
+ CLICK_FEEDBACK_DURATION);
+ mFastBitmapDrawable.mScaleAnimation.end();
+ assertEquals("End value not correct.",
+ (float) SCALE.get(mFastBitmapDrawable), PRESSED_SCALE, EPSILON);
+ assertTrue("Wrong interpolator used.",
+ mFastBitmapDrawable.mScaleAnimation.getInterpolator()
+ instanceof AccelerateInterpolator);
+ }
+
+ @Test
+ public void testOnStateChange_stateHoveredAndPressed() {
+ int[] state = new int[]{android.R.attr.state_hovered, android.R.attr.state_pressed};
+
+ boolean isHandled = mFastBitmapDrawable.onStateChange(state);
+
+ // Animate to pressed state only.
+ assertTrue("State change not handled.", isHandled);
+ assertEquals("Duration not correct.", mFastBitmapDrawable.mScaleAnimation.getDuration(),
+ CLICK_FEEDBACK_DURATION);
+ mFastBitmapDrawable.mScaleAnimation.end();
+ assertEquals("End value not correct.",
+ (float) SCALE.get(mFastBitmapDrawable), PRESSED_SCALE, EPSILON);
+ assertTrue("Wrong interpolator used.",
+ mFastBitmapDrawable.mScaleAnimation.getInterpolator()
+ instanceof AccelerateInterpolator);
+ }
+
+ @Test
+ public void testOnStateChange_stateHoveredAndPressedToPressed() {
+ mFastBitmapDrawable.mIsPressed = true;
+ mFastBitmapDrawable.mIsHovered = true;
+ SCALE.setValue(mFastBitmapDrawable, PRESSED_SCALE);
+ int[] state = new int[]{android.R.attr.state_pressed};
+
+ boolean isHandled = mFastBitmapDrawable.onStateChange(state);
+
+ // No scale change from pressed state to pressed state.
+ assertTrue("State not changed.", isHandled);
+ assertEquals("End value not correct.",
+ (float) SCALE.get(mFastBitmapDrawable), PRESSED_SCALE, EPSILON);
+ }
+
+ @Test
+ public void testOnStateChange_stateHoveredAndPressedToHovered() {
+ mFastBitmapDrawable.mIsPressed = true;
+ mFastBitmapDrawable.mIsHovered = true;
+ SCALE.setValue(mFastBitmapDrawable, PRESSED_SCALE);
+ int[] state = new int[]{android.R.attr.state_hovered};
+
+ boolean isHandled = mFastBitmapDrawable.onStateChange(state);
+
+ // No scale change from pressed state to hovered state.
+ assertTrue("State not changed.", isHandled);
+ assertEquals("End value not correct.",
+ (float) SCALE.get(mFastBitmapDrawable), HOVERED_SCALE, EPSILON);
+ }
+
+ @Test
+ public void testOnStateChange_stateHoveredToPressed() {
+ mFastBitmapDrawable.mIsHovered = true;
+ SCALE.setValue(mFastBitmapDrawable, HOVERED_SCALE);
+ int[] state = new int[]{android.R.attr.state_pressed};
+
+ boolean isHandled = mFastBitmapDrawable.onStateChange(state);
+
+ // No scale change from pressed state to hovered state.
+ assertTrue("State not changed.", isHandled);
+ assertEquals("End value not correct.",
+ (float) SCALE.get(mFastBitmapDrawable), PRESSED_SCALE, EPSILON);
+ }
+
+ @Test
+ public void testOnStateChange_statePressedToHovered() {
+ mFastBitmapDrawable.mIsPressed = true;
+ SCALE.setValue(mFastBitmapDrawable, PRESSED_SCALE);
+ int[] state = new int[]{android.R.attr.state_hovered};
+
+ boolean isHandled = mFastBitmapDrawable.onStateChange(state);
+
+ // No scale change from pressed state to hovered state.
+ assertTrue("State not changed.", isHandled);
+ assertEquals("End value not correct.",
+ (float) SCALE.get(mFastBitmapDrawable), HOVERED_SCALE, EPSILON);
+ }
+
+ @Test
+ public void testOnStateChange_stateDefaultFromPressed() {
+ mFastBitmapDrawable.mIsPressed = true;
+ SCALE.setValue(mFastBitmapDrawable, PRESSED_SCALE);
+ int[] state = new int[]{};
+
+ boolean isHandled = mFastBitmapDrawable.onStateChange(state);
+
+ // Animate to default state from pressed state.
+ assertTrue("State change not handled.", isHandled);
+ assertEquals("Duration not correct.", mFastBitmapDrawable.mScaleAnimation.getDuration(),
+ CLICK_FEEDBACK_DURATION);
+ mFastBitmapDrawable.mScaleAnimation.end();
+ assertEquals("End value not correct.", (float) SCALE.get(mFastBitmapDrawable), 1f, EPSILON);
+ assertTrue("Wrong interpolator used.",
+ mFastBitmapDrawable.mScaleAnimation.getInterpolator()
+ instanceof DecelerateInterpolator);
+ }
+
+ @Test
+ public void testOnStateChange_stateDefaultFromHovered() {
+ mFastBitmapDrawable.mIsHovered = true;
+ SCALE.setValue(mFastBitmapDrawable, HOVERED_SCALE);
+ int[] state = new int[]{};
+
+ boolean isHandled = mFastBitmapDrawable.onStateChange(state);
+
+ // Animate to default state from hovered state.
+ assertTrue("State change not handled.", isHandled);
+ assertEquals("Duration not correct.", mFastBitmapDrawable.mScaleAnimation.getDuration(),
+ HOVER_FEEDBACK_DURATION);
+ mFastBitmapDrawable.mScaleAnimation.end();
+ assertEquals("End value not correct.", (float) SCALE.get(mFastBitmapDrawable), 1f, EPSILON);
+ assertTrue("Wrong interpolator used.",
+ mFastBitmapDrawable.mScaleAnimation.getInterpolator() instanceof PathInterpolator);
+ }
+
+ @Test
+ public void testOnStateChange_stateHoveredWhilePartiallyScaled() {
+ SCALE.setValue(mFastBitmapDrawable, 0.5f);
+ int[] state = new int[]{android.R.attr.state_hovered};
+
+ boolean isHandled = mFastBitmapDrawable.onStateChange(state);
+
+ // Animate to hovered state from midway to pressed state.
+ assertTrue("State change not handled.", isHandled);
+ assertEquals("Duration not correct.",
+ mFastBitmapDrawable.mScaleAnimation.getDuration(), HOVER_FEEDBACK_DURATION);
+ mFastBitmapDrawable.mScaleAnimation.end();
+ assertEquals("End value not correct.",
+ (float) SCALE.get(mFastBitmapDrawable), HOVERED_SCALE, EPSILON);
+ assertTrue("Wrong interpolator used.",
+ mFastBitmapDrawable.mScaleAnimation.getInterpolator() instanceof PathInterpolator);
+ }
+
+ @Test
+ public void testOnStateChange_statePressedWhilePartiallyScaled() {
+ SCALE.setValue(mFastBitmapDrawable, 0.5f);
+ int[] state = new int[]{android.R.attr.state_pressed};
+
+ boolean isHandled = mFastBitmapDrawable.onStateChange(state);
+
+ // Animate to pressed state from midway to hovered state.
+ assertTrue("State change not handled.", isHandled);
+ assertEquals("Duration not correct.",
+ mFastBitmapDrawable.mScaleAnimation.getDuration(), CLICK_FEEDBACK_DURATION);
+ mFastBitmapDrawable.mScaleAnimation.end();
+ assertEquals("End value not correct.",
+ (float) SCALE.get(mFastBitmapDrawable), PRESSED_SCALE, EPSILON);
+ assertTrue("Wrong interpolator used.",
+ mFastBitmapDrawable.mScaleAnimation.getInterpolator()
+ instanceof AccelerateInterpolator);
+ }
+
+ @Test
+ public void testOnStateChange_stateDefaultFromPressedNotVisible() {
+ when(mFastBitmapDrawable.isVisible()).thenReturn(false);
+ mFastBitmapDrawable.mIsPressed = true;
+ SCALE.setValue(mFastBitmapDrawable, PRESSED_SCALE);
+ clearInvocations(mFastBitmapDrawable);
+ int[] state = new int[]{};
+
+ boolean isHandled = mFastBitmapDrawable.onStateChange(state);
+
+ // No animations when state was pressed but drawable no longer visible. Set values directly.
+ assertTrue("State change not handled.", isHandled);
+ assertNull("Scale animation not null.", mFastBitmapDrawable.mScaleAnimation);
+ assertEquals("End value not correct.", (float) SCALE.get(mFastBitmapDrawable), 1f, EPSILON);
+ verify(mFastBitmapDrawable).invalidateSelf();
+ }
+
+ @Test
+ public void testOnStateChange_stateDefaultFromHoveredNotVisible() {
+ when(mFastBitmapDrawable.isVisible()).thenReturn(false);
+ mFastBitmapDrawable.mIsHovered = true;
+ SCALE.setValue(mFastBitmapDrawable, HOVERED_SCALE);
+ clearInvocations(mFastBitmapDrawable);
+ int[] state = new int[]{};
+
+ boolean isHandled = mFastBitmapDrawable.onStateChange(state);
+
+ // No animations when state was hovered but drawable no longer visible. Set values directly.
+ assertTrue("State change not handled.", isHandled);
+ assertNull("Scale animation not null.", mFastBitmapDrawable.mScaleAnimation);
+ assertEquals("End value not correct.", (float) SCALE.get(mFastBitmapDrawable), 1f, EPSILON);
+ verify(mFastBitmapDrawable).invalidateSelf();
+ }
+}
diff --git a/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt b/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt
index 2a27487..d102397 100644
--- a/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt
+++ b/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt
@@ -158,4 +158,25 @@
assertThat(dp.isQsbInline).isFalse()
assertThat(dp.hotseatQsbWidth).isEqualTo(1095)
}
+
+ @Test
+ fun border_space_should_be_zero_when_numHotseatIcons_is_smallerOrEqual_1() {
+ initializeVarsForTablet(isGestureMode = false)
+ windowBounds = WindowBounds(Rect(0, 0, 1800, 2560), Rect(0, 104, 0, 0))
+
+ val numShownHotseatIcons = listOf(-1, 0, 1)
+ for (numHotseatIcons in numShownHotseatIcons) {
+ inv?.numShownHotseatIcons = numHotseatIcons
+
+ val dp = newDP()
+ dp.isTaskbarPresentInApps = true
+
+ assertThat(dp.numShownHotseatIcons).isEqualTo(numHotseatIcons)
+ assertThat(dp.hotseatBorderSpace).isEqualTo(0)
+
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(177)
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(177)
+ assertThat(dp.hotseatQsbWidth).isEqualTo(1445)
+ }
+ }
}
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 1ade1b0..e12cf2d 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -19,6 +19,8 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static com.android.launcher3.testing.shared.TestProtocol.ICON_MISSING;
+import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
+import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
import static com.google.common.truth.Truth.assertThat;
@@ -61,6 +63,7 @@
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
import com.android.launcher3.util.rule.TISBindRule;
+import com.android.launcher3.util.rule.TestStabilityRule.Stability;
import com.android.launcher3.widget.picker.WidgetsFullSheet;
import com.android.launcher3.widget.picker.WidgetsRecyclerView;
@@ -329,7 +332,8 @@
}
@Test
- @Ignore // b/293191790
+ @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/293191790
+ @ScreenRecord
@PortraitLandscape
public void testWidgets() throws Exception {
// Test opening widgets.
@@ -391,6 +395,28 @@
}
}
+ @Test
+ public void testLaunchHomeScreenMenuItem() {
+ // Drag the test app icon to home screen and open short cut menu from the icon
+ final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+ allApps.freeze();
+ try {
+ allApps.getAppIcon(APP_NAME).dragToWorkspace(false, false);
+ final AppIconMenu menu = mLauncher.getWorkspace().getWorkspaceAppIcon(
+ APP_NAME).openDeepShortcutMenu();
+
+ executeOnLauncher(
+ launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu",
+ isOptionsPopupVisible(launcher)));
+
+ final AppIconMenuItem menuItem = menu.getMenuItem(1);
+ assertEquals("Wrong menu item", "Shortcut 2", menuItem.getText());
+ menuItem.launch(getAppPackageName());
+ } finally {
+ allApps.unfreeze();
+ }
+ }
+
@PlatinumTest(focusArea = "launcher")
@Test
@PortraitLandscape
@@ -531,6 +557,7 @@
@Test
@PortraitLandscape
@PlatinumTest(focusArea = "launcher")
+ @ScreenRecord // TODO(b/293944634): Remove after flaky debug
public void testUninstallFromWorkspace() throws Exception {
installDummyAppAndWaitForUIUpdate();
try {
diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
index 4580082..261436b 100644
--- a/tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -227,7 +227,8 @@
UserCache.INSTANCE, InstallSessionHelper.INSTANCE, LauncherPrefs.INSTANCE,
LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
DisplayController.INSTANCE, CustomWidgetManager.INSTANCE,
- SettingsCache.INSTANCE, PluginManagerWrapper.INSTANCE, LockedUserState.INSTANCE,
+ SettingsCache.INSTANCE, PluginManagerWrapper.INSTANCE,
+ LockedUserState.INSTANCE, WallpaperColorHints.INSTANCE,
ItemInstallQueue.INSTANCE, WindowManagerProxy.INSTANCE);
// System settings cache content provider. Ensure that they are statically initialized
diff --git a/tests/src/com/android/launcher3/util/TestUtil.java b/tests/src/com/android/launcher3/util/TestUtil.java
index 4cd6c53..21059e6 100644
--- a/tests/src/com/android/launcher3/util/TestUtil.java
+++ b/tests/src/com/android/launcher3/util/TestUtil.java
@@ -67,6 +67,7 @@
private static final String TAG = "TestUtil";
public static final String DUMMY_PACKAGE = "com.example.android.aardwolf";
+ public static final String DUMMY_CLASS_NAME = "com.example.android.aardwolf.Activity1";
public static final long DEFAULT_UI_TIMEOUT = 10000;
public static void installDummyApp() throws IOException {
diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java
index eef1bc8..388a59a 100644
--- a/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java
+++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java
@@ -38,9 +38,6 @@
private static final IgnoreNode IGNORED_NODES_ROOT = buildIgnoreNodesTree(List.of(
CONTENT + "LauncherRootView:id/launcher|FloatingIconView",
- DRAG_LAYER
- + "SearchContainerView:id/apps_view|AllAppsRecyclerView:id/apps_list_view"
- + "|BubbleTextView:id/icon",
DRAG_LAYER + "LauncherRecentsView:id/overview_panel|TaskView|TextView",
DRAG_LAYER
+ "LauncherAllAppsContainerView:id/apps_view|AllAppsRecyclerView:id"
@@ -58,7 +55,11 @@
+ "|WidgetCellPreview:id/widget_preview_container|ImageView:id/widget_badge",
RECENTS_DRAG_LAYER + "FallbackRecentsView:id/overview_panel|TaskView|IconView:id/icon",
DRAG_LAYER + "SearchContainerView:id/apps_view",
- DRAG_LAYER + "LauncherDragView"
+ DRAG_LAYER + "LauncherDragView",
+ DRAG_LAYER + "FloatingTaskView|FloatingTaskThumbnailView:id/thumbnail",
+ DRAG_LAYER
+ + "WidgetsFullSheet|SpringRelativeLayout:id/container|WidgetsRecyclerView:id"
+ + "/primary_widgets_list_view|WidgetsListHeader:id/widgets_list_header"
));
// Per-AnalysisNode data that's specific to this detector.
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 23d09d4..fb08ea4 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -336,4 +336,11 @@
final Bundle testInfo = mLauncher.getTestInfo(TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS);
return testInfo == null ? 0 : testInfo.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
+
+ /**
+ * Return the QSB UI object on the AllApps screen.
+ * @return the QSB UI object.
+ */
+ @NonNull
+ public abstract Qsb getQsb();
}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java b/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
index c4744a1..0e0291f 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
@@ -62,4 +62,10 @@
return mLauncher.getTestInfo(TestProtocol.REQUEST_TASKBAR_APPS_LIST_SCROLL_Y)
.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
+
+ @NonNull
+ @Override
+ public TaskbarAllAppsQsb getQsb() {
+ return new TaskbarAllAppsQsb(mLauncher, verifyActiveContainer());
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsQsb.java b/tests/tapl/com/android/launcher3/tapl/AllAppsQsb.java
index 0931cd4..1692351 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllAppsQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllAppsQsb.java
@@ -22,16 +22,7 @@
*/
class AllAppsQsb extends Qsb {
- private final UiObject2 mAllAppsContainer;
-
AllAppsQsb(LauncherInstrumentation launcher, UiObject2 allAppsContainer) {
- super(launcher);
- mAllAppsContainer = allAppsContainer;
- waitForQsbObject();
- }
-
- @Override
- protected UiObject2 waitForQsbObject() {
- return mLauncher.waitForObjectInContainer(mAllAppsContainer, "search_container_all_apps");
+ super(launcher, allAppsContainer, "search_container_all_apps");
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
index a03472a..33c6334 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
@@ -117,11 +117,8 @@
}
}
- /**
- * Return the QSB UI object on the AllApps screen.
- * @return the QSB UI object.
- */
@NonNull
+ @Override
public Qsb getQsb() {
return new AllAppsQsb(mLauncher, verifyActiveContainer());
}
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
index 20d09a1..5385c65 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
@@ -22,16 +22,7 @@
*/
class HomeQsb extends Qsb {
- private final UiObject2 mHotSeat;
-
HomeQsb(LauncherInstrumentation launcher, UiObject2 hotseat) {
- super(launcher);
- mHotSeat = hotseat;
- waitForQsbObject();
- }
-
- @Override
- protected UiObject2 waitForQsbObject() {
- return mLauncher.waitForObjectInContainer(mHotSeat, "search_container_hotseat");
+ super(launcher, hotseat, "search_container_hotseat");
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
index a05b499..9a7710a 100644
--- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
+++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
@@ -224,12 +224,14 @@
int leftEdge = 10;
Point taskbarUnstashArea = new Point(leftEdge, mLauncher.getRealDisplaySize().y - 1);
mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_ENTER,
- new Point(taskbarUnstashArea.x, taskbarUnstashArea.y), null);
+ new Point(taskbarUnstashArea.x, taskbarUnstashArea.y), null,
+ InputDevice.SOURCE_MOUSE);
mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID);
mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_EXIT,
- new Point(taskbarUnstashArea.x, taskbarUnstashArea.y), null);
+ new Point(taskbarUnstashArea.x, taskbarUnstashArea.y), null,
+ InputDevice.SOURCE_MOUSE);
return new Taskbar(mLauncher);
}
@@ -246,7 +248,8 @@
Point stashedTaskbarHintArea = new Point(mLauncher.getRealDisplaySize().x / 2,
mLauncher.getRealDisplaySize().y - 1);
mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_ENTER,
- new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), null);
+ new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), null,
+ InputDevice.SOURCE_MOUSE);
mLauncher.getDevice().wait(mStashedTaskbarHintScaleCondition,
LauncherInstrumentation.WAIT_TIME_MS);
@@ -257,7 +260,8 @@
Point taskbarUnstashArea = new Point(mLauncher.getRealDisplaySize().x / 2,
mLauncher.getRealDisplaySize().y - 1);
mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_EXIT,
- new Point(taskbarUnstashArea.x, taskbarUnstashArea.y), null);
+ new Point(taskbarUnstashArea.x, taskbarUnstashArea.y), null,
+ InputDevice.SOURCE_MOUSE);
mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID);
return new Taskbar(mLauncher);
diff --git a/tests/tapl/com/android/launcher3/tapl/Qsb.java b/tests/tapl/com/android/launcher3/tapl/Qsb.java
index 6bc4f21..7f3f61d 100644
--- a/tests/tapl/com/android/launcher3/tapl/Qsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/Qsb.java
@@ -30,13 +30,21 @@
private static final String ASSISTANT_APP_PACKAGE = "com.google.android.googlequicksearchbox";
private static final String ASSISTANT_ICON_RES_ID = "mic_icon";
protected final LauncherInstrumentation mLauncher;
+ private final UiObject2 mContainer;
+ private final String mQsbResName;
- protected Qsb(LauncherInstrumentation launcher) {
+ protected Qsb(LauncherInstrumentation launcher, UiObject2 container, String qsbResName) {
mLauncher = launcher;
+ mContainer = container;
+ mQsbResName = qsbResName;
+ waitForQsbObject();
}
// Waits for the quick search box.
- protected abstract UiObject2 waitForQsbObject();
+ private UiObject2 waitForQsbObject() {
+ return mLauncher.waitForObjectInContainer(mContainer, mQsbResName);
+ }
+
/**
* Launch assistant app by tapping mic icon on qsb.
*/
@@ -79,8 +87,12 @@
mLauncher.waitForIdle();
try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
"clicked qsb to open search result page")) {
- return new SearchResultFromQsb(mLauncher);
+ return createSearchResult();
}
}
}
+
+ protected SearchResultFromQsb createSearchResult() {
+ return new SearchResultFromQsb(mLauncher);
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
index 80176e9..8c3402f 100644
--- a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
@@ -32,7 +32,7 @@
// This particular ID change should happen with caution
private static final String SEARCH_CONTAINER_RES_ID = "search_results_list_view";
- private final LauncherInstrumentation mLauncher;
+ protected final LauncherInstrumentation mLauncher;
SearchResultFromQsb(LauncherInstrumentation launcher) {
mLauncher = launcher;
@@ -49,8 +49,12 @@
}
/** Find the app from search results with app name. */
- public Launchable findAppIcon(String appName) {
+ public AppIcon findAppIcon(String appName) {
UiObject2 icon = mLauncher.waitForLauncherObject(By.clazz(TextView.class).text(appName));
+ return createAppIcon(icon);
+ }
+
+ protected AppIcon createAppIcon(UiObject2 icon) {
return new AllAppsAppIcon(mLauncher, icon);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java
new file mode 100644
index 0000000..c267c9e
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 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;
+
+/**
+ * Operations on search result page opened from Taskbar qsb.
+ */
+public class SearchResultFromTaskbarQsb extends SearchResultFromQsb {
+
+ SearchResultFromTaskbarQsb(LauncherInstrumentation launcher) {
+ super(launcher);
+ }
+
+ @Override
+ public TaskbarAppIcon findAppIcon(String appName) {
+ return (TaskbarAppIcon) super.findAppIcon(appName);
+ }
+
+ @Override
+ protected TaskbarAppIcon createAppIcon(UiObject2 icon) {
+ return new TaskbarAppIcon(mLauncher, icon);
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/TaskbarAllAppsQsb.java b/tests/tapl/com/android/launcher3/tapl/TaskbarAllAppsQsb.java
new file mode 100644
index 0000000..7cecd3e
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/TaskbarAllAppsQsb.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 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;
+
+/**
+ * Operations on Taskbar AllApp screen qsb.
+ */
+public class TaskbarAllAppsQsb extends Qsb {
+
+ TaskbarAllAppsQsb(LauncherInstrumentation launcher, UiObject2 allAppsContainer) {
+ super(launcher, allAppsContainer, "search_container_all_apps");
+ }
+
+ @Override
+ public SearchResultFromTaskbarQsb showSearchResult() {
+ return (SearchResultFromTaskbarQsb) super.showSearchResult();
+ }
+
+ @Override
+ protected SearchResultFromTaskbarQsb createSearchResult() {
+ return new SearchResultFromTaskbarQsb(mLauncher);
+ }
+}