Merge "Refresh `mNumAppsPerRowAllApps` when device profile changes." into udc-qpr-dev
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index bd47923..5691ecf 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -19,7 +19,6 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_DENY;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_SEEN;
-import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -30,7 +29,6 @@
import android.widget.LinearLayout;
import android.widget.TextView;
-import com.android.app.animation.Interpolators;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
@@ -164,10 +162,7 @@
return;
}
mIsOpen = true;
- mOpenCloseAnimator.setValues(
- PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
- mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- mOpenCloseAnimator.start();
+ setUpDefaultOpenAnimator().start();
}
@Override
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index d379d6d..882682d 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -193,5 +193,6 @@
writer.println(prefix + "\tmIgnoreStateChangesDuringMultiWindowAnimation="
+ mIgnoreStateChangesDuringMultiWindowAnimation);
writer.println(prefix + "\tmPauseBlurs=" + mPauseBlurs);
+ writer.println(prefix + "\tmWaitingOnSurfaceValidity=" + mWaitingOnSurfaceValidity);
}
}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index 7283a18..ecf483c 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -27,6 +27,7 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.quickstep.GestureState;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.views.DesktopAppSelectView;
import com.android.wm.shell.desktopmode.IDesktopTaskListener;
@@ -39,7 +40,8 @@
private static final String TAG = "DesktopVisController";
private static final boolean DEBUG = false;
-
+ private static final boolean IS_STASHING_ENABLED = SystemProperties.getBoolean(
+ "persist.wm.debug.desktop_stashing", false);
private final Launcher mLauncher;
private boolean mFreeformTasksVisible;
@@ -73,6 +75,9 @@
@Override
public void onStashedChanged(int displayId, boolean stashed) {
+ if (!IS_STASHING_ENABLED) {
+ return;
+ }
MAIN_EXECUTOR.execute(() -> {
if (displayId == mLauncher.getDisplayId()) {
if (DEBUG) {
@@ -166,20 +171,40 @@
/**
* Whether recents gesture is currently in progress.
*/
- public boolean isGestureInProgress() {
+ public boolean isRecentsGestureInProgress() {
return mGestureInProgress;
}
/**
- * Sets whether recents gesture is in progress.
+ * Notify controller that recents gesture has started.
*/
- public void setGestureInProgress(boolean gestureInProgress) {
- if (DEBUG) {
- Log.d(TAG, "setGestureInProgress: inProgress=" + gestureInProgress);
- }
+ public void setRecentsGestureStart() {
if (!isDesktopModeSupported()) {
return;
}
+ setRecentsGestureInProgress(true);
+ }
+
+ /**
+ * Notify controller that recents gesture finished with the given
+ * {@link com.android.quickstep.GestureState.GestureEndTarget}
+ */
+ public void setRecentsGestureEnd(@Nullable GestureState.GestureEndTarget endTarget) {
+ if (!isDesktopModeSupported()) {
+ return;
+ }
+ setRecentsGestureInProgress(false);
+
+ if (endTarget == null) {
+ // Gesture did not result in a new end target. Ensure launchers gets paused again.
+ markLauncherPaused();
+ }
+ }
+
+ private void setRecentsGestureInProgress(boolean gestureInProgress) {
+ if (DEBUG) {
+ Log.d(TAG, "setGestureInProgress: inProgress=" + gestureInProgress);
+ }
if (gestureInProgress != mGestureInProgress) {
mGestureInProgress = gestureInProgress;
}
@@ -189,7 +214,7 @@
* Handle launcher moving to home due to home gesture or home button press.
*/
public void onHomeActionTriggered() {
- if (areFreeformTasksVisible()) {
+ if (IS_STASHING_ENABLED && areFreeformTasksVisible()) {
SystemUiProxy.INSTANCE.get(mLauncher).stashDesktopApps(mLauncher.getDisplayId());
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index cb9c329..f7f3bfd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -146,7 +146,7 @@
private int mLastRequestedNonFullscreenHeight;
private NavigationMode mNavMode;
- private final boolean mImeDrawsImeNavBar;
+ private boolean mImeDrawsImeNavBar;
private final ViewCache mViewCache = new ViewCache();
private final boolean mIsSafeModeEnabled;
@@ -299,6 +299,7 @@
public void init(@NonNull TaskbarSharedState sharedState) {
+ mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, getResources(), false);
mLastRequestedNonFullscreenHeight = getDefaultTaskbarWindowHeight();
mWindowLayoutParams = createAllWindowParams();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java
index 363f915..c3ec1e5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java
@@ -35,6 +35,8 @@
import android.view.MotionEvent;
import android.view.View;
+import androidx.annotation.VisibleForTesting;
+
import com.android.app.animation.Interpolators;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
@@ -49,7 +51,7 @@
*/
public class TaskbarHoverToolTipController implements View.OnHoverListener {
- private static final int HOVER_TOOL_TIP_REVEAL_START_DELAY = 400;
+ @VisibleForTesting protected static final int HOVER_TOOL_TIP_REVEAL_START_DELAY = 400;
private static final int HOVER_TOOL_TIP_REVEAL_DURATION = 300;
private static final int HOVER_TOOL_TIP_EXIT_DURATION = 150;
@@ -145,7 +147,6 @@
}
private void startRevealHoverToolTip() {
- mActivity.setTaskbarWindowFullscreen(true);
mHoverToolTipHandler.postDelayed(mRevealHoverToolTipRunnable,
HOVER_TOOL_TIP_REVEAL_START_DELAY);
}
@@ -157,6 +158,7 @@
if (mHoverView instanceof FolderIcon && !((FolderIcon) mHoverView).getIconVisible()) {
return;
}
+ mActivity.setTaskbarWindowFullscreen(true);
Rect iconViewBounds = Utilities.getViewBounds(mHoverView);
mHoverToolTipView.showAtLocation(mToolTipText, iconViewBounds.centerX(),
mTaskbarView.getTop(), /* shouldAutoClose= */ false);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 68ea475..a935bac 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -94,26 +94,12 @@
} else {
0
}
- if (context.isGestureNav) {
- windowLayoutParams.providedInsets =
- arrayOf(
- InsetsFrameProvider(insetsOwner, 0, navigationBars())
- .setFlags(
- FLAG_SUPPRESS_SCRIM or insetsRoundedCornerFlag,
- FLAG_SUPPRESS_SCRIM or FLAG_INSETS_ROUNDED_CORNER
- ),
- InsetsFrameProvider(insetsOwner, 0, tappableElement()),
- InsetsFrameProvider(insetsOwner, 0, mandatorySystemGestures()),
- InsetsFrameProvider(insetsOwner, INDEX_LEFT, systemGestures())
- .setSource(SOURCE_DISPLAY),
- InsetsFrameProvider(insetsOwner, INDEX_RIGHT, systemGestures())
- .setSource(SOURCE_DISPLAY)
- )
- } else {
- windowLayoutParams.providedInsets = getButtonNavInsets(insetsRoundedCornerFlag)
+
+ windowLayoutParams.providedInsets = getProvidedInsets(insetsRoundedCornerFlag)
+ if (!context.isGestureNav) {
if (windowLayoutParams.paramsForRotation != null) {
for (layoutParams in windowLayoutParams.paramsForRotation) {
- layoutParams.providedInsets = getButtonNavInsets(insetsRoundedCornerFlag)
+ layoutParams.providedInsets = getProvidedInsets(insetsRoundedCornerFlag)
}
}
}
@@ -165,15 +151,28 @@
context.notifyUpdateLayoutParams()
}
- private fun getButtonNavInsets(insetsRoundedCornerFlag: Int): Array<InsetsFrameProvider> {
+ /**
+ * The inset types and number of insets provided have to match for both gesture nav and button
+ * nav. The values and the order of the elements in array are allowed to differ.
+ * Reason being WM does not allow types and number of insets changing for a given window once it
+ * is added into the hierarchy for performance reasons.
+ */
+ private fun getProvidedInsets(insetsRoundedCornerFlag: Int): Array<InsetsFrameProvider> {
+ val navBarsFlag =
+ (if (context.isGestureNav) FLAG_SUPPRESS_SCRIM else 0) or insetsRoundedCornerFlag
return arrayOf(
- InsetsFrameProvider(insetsOwner, 0, navigationBars())
+ InsetsFrameProvider(insetsOwner, 0, navigationBars())
.setFlags(
- insetsRoundedCornerFlag,
- (FLAG_SUPPRESS_SCRIM or FLAG_INSETS_ROUNDED_CORNER)
+ navBarsFlag,
+ FLAG_SUPPRESS_SCRIM or FLAG_INSETS_ROUNDED_CORNER
),
- InsetsFrameProvider(insetsOwner, 0, tappableElement()),
- InsetsFrameProvider(insetsOwner, 0, mandatorySystemGestures()))
+ InsetsFrameProvider(insetsOwner, 0, tappableElement()),
+ InsetsFrameProvider(insetsOwner, 0, mandatorySystemGestures()),
+ InsetsFrameProvider(insetsOwner, INDEX_LEFT, systemGestures())
+ .setSource(SOURCE_DISPLAY),
+ InsetsFrameProvider(insetsOwner, INDEX_RIGHT, systemGestures())
+ .setSource(SOURCE_DISPLAY)
+ )
}
private fun setProviderInsets(provider: InsetsFrameProvider, gravity: Int) {
@@ -181,47 +180,45 @@
val tappableHeight = controllers.taskbarStashController.tappableHeightToReportToApps
val res = context.resources
if (provider.type == navigationBars() || provider.type == mandatorySystemGestures()) {
- provider.insetsSize = getInsetsByNavMode(contentHeight, gravity)
+ provider.insetsSize = getInsetsForGravity(contentHeight, gravity)
} else if (provider.type == tappableElement()) {
- provider.insetsSize = getInsetsByNavMode(tappableHeight, gravity)
+ provider.insetsSize = getInsetsForGravity(tappableHeight, gravity)
} else if (provider.type == systemGestures() && provider.index == INDEX_LEFT) {
- provider.insetsSize =
- Insets.of(
- gestureNavSettingsObserver.getLeftSensitivityForCallingUser(res),
- 0,
- 0,
- 0
- )
+ val leftIndexInset =
+ if (context.isThreeButtonNav) 0
+ else gestureNavSettingsObserver.getLeftSensitivityForCallingUser(res)
+ provider.insetsSize = Insets.of(leftIndexInset, 0, 0, 0)
} else if (provider.type == systemGestures() && provider.index == INDEX_RIGHT) {
- provider.insetsSize =
- Insets.of(
- 0,
- 0,
- gestureNavSettingsObserver.getRightSensitivityForCallingUser(res),
- 0
- )
+ val rightIndexInset =
+ if (context.isThreeButtonNav) 0
+ else gestureNavSettingsObserver.getRightSensitivityForCallingUser(res)
+ provider.insetsSize = Insets.of(0, 0, rightIndexInset, 0)
}
- val imeInsetsSize = getInsetsByNavMode(taskbarHeightForIme, gravity)
- val insetsSizeOverride =
+
+ val imeInsetsSize = getInsetsForGravity(taskbarHeightForIme, gravity)
+ val imeInsetsSizeOverride =
arrayOf(
InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize),
)
// Use 0 tappableElement insets for the VoiceInteractionWindow when gesture nav is enabled.
- val visInsetsSizeForGestureNavTappableElement = getInsetsByNavMode(0, gravity)
- val insetsSizeOverrideForGestureNavTappableElement =
+ val visInsetsSizeForTappableElement =
+ if (context.isGestureNav) getInsetsForGravity(0, gravity)
+ else getInsetsForGravity(tappableHeight, gravity)
+ val insetsSizeOverrideForTappableElement =
arrayOf(
InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize),
InsetsFrameProvider.InsetsSizeOverride(
TYPE_VOICE_INTERACTION,
- visInsetsSizeForGestureNavTappableElement
+ visInsetsSizeForTappableElement
),
)
- if (context.isGestureNav && provider.type == tappableElement()) {
- provider.insetsSizeOverrides = insetsSizeOverrideForGestureNavTappableElement
+ if ((context.isGestureNav || TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+ && provider.type == tappableElement()) {
+ provider.insetsSizeOverrides = insetsSizeOverrideForTappableElement
} else if (provider.type != systemGestures()) {
// We only override insets at the bottom of the screen
- provider.insetsSizeOverrides = insetsSizeOverride
+ provider.insetsSizeOverrides = imeInsetsSizeOverride
}
}
@@ -229,14 +226,14 @@
* @return [Insets] where the [inset] is either used as a bottom inset or
* right/left inset if using 3 button nav
*/
- private fun getInsetsByNavMode(inset: Int, gravity: Int): Insets {
- if ((gravity and Gravity.BOTTOM) != 0) {
+ private fun getInsetsForGravity(inset: Int, gravity: Int): Insets {
+ if ((gravity and Gravity.BOTTOM) == Gravity.BOTTOM) {
// Taskbar or portrait phone mode
return Insets.of(0, 0, 0, inset)
}
// TODO(b/230394142): seascape
- val isSeascape = (gravity and Gravity.START) != 0
+ val isSeascape = (gravity and Gravity.START) == Gravity.START
val leftInset = if (isSeascape) inset else 0
val rightInset = if (isSeascape) 0 else inset
return Insets.of(leftInset , 0, rightInset, 0)
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 5e37cf4..b5b453b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -338,7 +338,6 @@
// us that we're paused until a bit later. This avoids flickering upon recreating taskbar.
updateStateForFlag(FLAG_IN_APP, true);
applyState(/* duration = */ 0);
-
notifyStashChange(/* visible */ false, /* stashed */ isStashedInApp());
}
@@ -675,7 +674,10 @@
.setDuration(duration));
mAnimator.play(mTaskbarImeBgAlpha.animateToValue(
hasAnyFlag(FLAG_STASHED_IN_APP_IME) ? 0 : 1).setDuration(duration));
- mAnimator.addListener(AnimatorListeners.forEndCallback(() -> mAnimator = null));
+ mAnimator.addListener(AnimatorListeners.forEndCallback(() -> {
+ mAnimator = null;
+ mIsStashed = isStashed;
+ }));
return;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index 4f75ef5..3fe7359 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -17,9 +17,6 @@
import static com.android.app.animation.Interpolators.EMPHASIZED;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
@@ -31,6 +28,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
+import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsViewController.TaskbarAllAppsCallbacks;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
@@ -68,16 +66,9 @@
mAllAppsCallbacks.onAllAppsTransitionStart(true);
if (animate) {
- mOpenCloseAnimator.setValues(
- PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
- mOpenCloseAnimator.setInterpolator(EMPHASIZED);
- mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mOpenCloseAnimator.removeListener(this);
- mAllAppsCallbacks.onAllAppsTransitionEnd(true);
- }
- });
+ setUpOpenCloseAnimator(TRANSLATION_SHIFT_OPENED, EMPHASIZED);
+ mOpenCloseAnimator.addListener(AnimatorListeners.forEndCallback(
+ () -> mAllAppsCallbacks.onAllAppsTransitionEnd(true)));
mOpenCloseAnimator.setDuration(mAllAppsCallbacks.getOpenDuration()).start();
} else {
mTranslationShift = TRANSLATION_SHIFT_OPENED;
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
index 7db2320..5c7f2be 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
@@ -67,6 +67,7 @@
val startContextualContainer =
navButtonsView.findViewById<ViewGroup>(ID_START_CONTEXTUAL_BUTTONS)
val isPhoneNavMode = phoneMode && isThreeButtonNav
+ val isPhoneGestureMode = phoneMode && !isThreeButtonNav
return when {
isPhoneNavMode -> {
if (!deviceProfile.isLandscape) {
@@ -92,6 +93,14 @@
)
}
}
+ isPhoneGestureMode ->{
+ PhoneGestureLayoutter(
+ resources,
+ navButtonContainer,
+ endContextualContainer,
+ startContextualContainer
+ )
+ }
deviceProfile.isTaskbarPresent -> {
return when {
isInSetup -> {
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
new file mode 100644
index 0000000..8525c6c
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.ViewGroup
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+
+/** Layoutter for showing gesture navigation on phone screen. No buttons here, no-op container */
+class PhoneGestureLayoutter(
+ resources: Resources,
+ navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup
+) :
+ AbstractNavButtonLayoutter(
+ resources,
+ navBarContainer,
+ endContextualContainer,
+ startContextualContainer
+ ) {
+
+ override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+ // no-op
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 1e1bff3..20383f4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -867,7 +867,7 @@
if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
DesktopVisibilityController controller = mDesktopVisibilityController;
if (controller != null && controller.areFreeformTasksVisible()
- && !controller.isGestureInProgress()) {
+ && !controller.isRecentsGestureInProgress()) {
// Return early to skip setting activity to appear as resumed
// TODO(b/255649902): shouldn't be needed when we have a separate launcher state
// for desktop that we can use to control other parts of launcher
diff --git a/quickstep/src/com/android/quickstep/util/BaseDepthController.java b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
index 931e468..99f564c 100644
--- a/quickstep/src/com/android/quickstep/util/BaseDepthController.java
+++ b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
@@ -18,6 +18,7 @@
import android.app.WallpaperManager;
import android.os.IBinder;
import android.util.FloatProperty;
+import android.util.Log;
import android.view.AttachedSurfaceControl;
import android.view.SurfaceControl;
@@ -50,6 +51,9 @@
private static final int DEPTH_INDEX_WIDGET = 1;
private static final int DEPTH_INDEX_COUNT = 2;
+ // b/291401432
+ private static final String TAG = "BaseDepthController";
+
protected final Launcher mLauncher;
/** Property to set the depth for state transition. */
public final MultiProperty stateDepth;
@@ -88,7 +92,7 @@
*/
protected boolean mInEarlyWakeUp;
- private boolean mWaitingOnSurfaceValidity;
+ protected boolean mWaitingOnSurfaceValidity;
public BaseDepthController(Launcher activity) {
mLauncher = activity;
@@ -133,9 +137,11 @@
return;
}
if (mSurface == null) {
+ Log.d(TAG, "mSurface is null and mCurrentBlur is: " + mCurrentBlur);
return;
}
if (!mSurface.isValid()) {
+ Log.d(TAG, "mSurface is not valid");
mWaitingOnSurfaceValidity = true;
onInvalidSurface();
return;
@@ -186,6 +192,8 @@
protected void setSurface(SurfaceControl surface) {
if (mSurface != surface || mWaitingOnSurfaceValidity) {
mSurface = surface;
+ Log.d(TAG, "setSurface:\n\tmWaitingOnSurfaceValidity: " + mWaitingOnSurfaceValidity
+ + "\n\tmSurface: " + mSurface);
applyDepthAndBlur();
}
}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
index 1cfaf14..83e9945 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -51,6 +51,7 @@
import com.android.quickstep.util.RecentsOrientedState;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.QuickStepContract;
import java.util.ArrayList;
import java.util.Arrays;
@@ -79,7 +80,7 @@
private static final String TAG = DesktopTaskView.class.getSimpleName();
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
@NonNull
private List<Task> mTasks = new ArrayList<>();
@@ -91,6 +92,8 @@
private final ArrayList<CancellableTask<?>> mPendingThumbnailRequests = new ArrayList<>();
+ private final TaskView.FullscreenDrawParams mSnapshotDrawParams;
+
private View mBackgroundView;
public DesktopTaskView(Context context) {
@@ -103,6 +106,10 @@
public DesktopTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
+
+ mSnapshotDrawParams = new FullscreenDrawParams(
+ QuickStepContract.getWindowCornerRadius(context),
+ QuickStepContract.getWindowCornerRadius(context));
}
@Override
@@ -465,14 +472,20 @@
for (int i = 0; i < mSnapshotViewMap.size(); i++) {
TaskThumbnailView thumbnailView = mSnapshotViewMap.valueAt(i);
thumbnailView.getTaskOverlay().setFullscreenProgress(progress);
- updateSnapshotRadius();
}
+ updateSnapshotRadius();
}
@Override
protected void updateSnapshotRadius() {
+ super.updateSnapshotRadius();
for (int i = 0; i < mSnapshotViewMap.size(); i++) {
- mSnapshotViewMap.valueAt(i).setFullscreenParams(mCurrentFullscreenParams);
+ if (i == 0) {
+ // All snapshots share the same params. Only update it with the first snapshot.
+ updateFullscreenParams(mSnapshotDrawParams,
+ mSnapshotView.getPreviewPositionHelper());
+ }
+ mSnapshotViewMap.valueAt(i).setFullscreenParams(mSnapshotDrawParams);
}
}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 5e488cc..4f119c0 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -249,7 +249,7 @@
DesktopVisibilityController desktopVisibilityController =
mActivity.getDesktopVisibilityController();
if (desktopVisibilityController != null) {
- desktopVisibilityController.setGestureInProgress(true);
+ desktopVisibilityController.setRecentsGestureStart();
}
}
@@ -257,9 +257,11 @@
public void onGestureAnimationEnd() {
DesktopVisibilityController desktopVisibilityController = null;
boolean showDesktopApps = false;
+ GestureState.GestureEndTarget endTarget = null;
if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
desktopVisibilityController = mActivity.getDesktopVisibilityController();
- if (mCurrentGestureEndTarget == GestureState.GestureEndTarget.LAST_TASK
+ endTarget = mCurrentGestureEndTarget;
+ if (endTarget == GestureState.GestureEndTarget.LAST_TASK
&& desktopVisibilityController.areFreeformTasksVisible()) {
// Recents gesture was cancelled and we are returning to the previous task.
// After super class has handled clean up, show desktop apps on top again
@@ -268,7 +270,7 @@
}
super.onGestureAnimationEnd();
if (desktopVisibilityController != null) {
- desktopVisibilityController.setGestureInProgress(false);
+ desktopVisibilityController.setRecentsGestureEnd(endTarget);
}
if (showDesktopApps) {
SystemUiProxy.INSTANCE.get(mActivity).showDesktopApps(mActivity.getDisplayId());
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 754d1ff..bfb578b 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -263,6 +263,13 @@
}
/**
+ * Returns the visibility of the overview actions buttons.
+ */
+ public @Visibility int getActionsButtonVisibility() {
+ return findViewById(R.id.action_buttons).getVisibility();
+ }
+
+ /**
* Offsets OverviewActionsView horizontal position based on 3 button nav container in taskbar.
*/
private void updatePadding() {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 6b0843c..854c3c7 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -1707,10 +1707,15 @@
}
void updateCurrentFullscreenParams(PreviewPositionHelper previewPositionHelper) {
+ updateFullscreenParams(mCurrentFullscreenParams, previewPositionHelper);
+ }
+
+ protected void updateFullscreenParams(TaskView.FullscreenDrawParams fullscreenParams,
+ PreviewPositionHelper previewPositionHelper) {
if (getRecentsView() == null) {
return;
}
- mCurrentFullscreenParams.setProgress(mFullscreenProgress, getRecentsView().getScaleX(),
+ fullscreenParams.setProgress(mFullscreenProgress, getRecentsView().getScaleX(),
getScaleX(), getWidth(), mActivity.getDeviceProfile(), previewPositionHelper);
}
@@ -1860,9 +1865,12 @@
public float mCurrentDrawnCornerRadius;
public FullscreenDrawParams(Context context) {
- mCornerRadius = TaskCornerRadius.get(context);
- mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context);
+ this(TaskCornerRadius.get(context), QuickStepContract.getWindowCornerRadius(context));
+ }
+ FullscreenDrawParams(float cornerRadius, float windowCornerRadius) {
+ mCornerRadius = cornerRadius;
+ mWindowCornerRadius = windowCornerRadius;
mCurrentDrawnCornerRadius = mCornerRadius;
}
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java
index 82849be..6c0d44d 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java
@@ -18,6 +18,7 @@
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS;
+import static com.android.launcher3.taskbar.TaskbarHoverToolTipController.HOVER_TOOL_TIP_REVEAL_START_DELAY;
import static com.google.common.truth.Truth.assertThat;
@@ -26,6 +27,7 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -56,7 +58,7 @@
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class TaskbarHoverToolTipControllerTest extends TaskbarBaseTestCase {
private TaskbarHoverToolTipController mTaskbarHoverToolTipController;
@@ -126,8 +128,10 @@
boolean hoverHandled =
mTaskbarHoverToolTipController.onHover(mHoverBubbleTextView, mMotionEvent);
- waitForIdleSync();
+ // Verify fullscreen is not set until the delayed runnable to reveal the tooltip has run
+ verify(taskbarActivityContext, never()).setTaskbarWindowFullscreen(true);
+ waitForIdleSync();
assertThat(hoverHandled).isTrue();
verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS,
true);
@@ -155,8 +159,10 @@
boolean hoverHandled =
mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent);
- waitForIdleSync();
+ // Verify fullscreen is not set until the delayed runnable to reveal the tooltip has run
+ verify(taskbarActivityContext, never()).setTaskbarWindowFullscreen(true);
+ waitForIdleSync();
assertThat(hoverHandled).isTrue();
verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS,
true);
@@ -216,6 +222,7 @@
}
private void waitForIdleSync() {
+ mTestableLooper.moveTimeForward(HOVER_TOOL_TIP_REVEAL_START_DELAY + 1);
mTestableLooper.processAllMessages();
}
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 06ce0e5..9d188ed 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -44,6 +44,7 @@
import com.android.launcher3.tapl.Overview;
import com.android.launcher3.tapl.OverviewActions;
import com.android.launcher3.tapl.OverviewTask;
+import com.android.launcher3.tapl.OverviewTaskMenu;
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.ui.TaplTestsLauncher3;
import com.android.launcher3.util.DisplayController;
@@ -196,6 +197,19 @@
actionsView.clickAndDismissScreenshot();
}
+
+ @Test
+ public void testOverviewActionsMenu() throws Exception {
+ startTestAppsWithCheck();
+
+ OverviewTaskMenu menu = mLauncher.goHome().switchToOverview().getCurrentTask().tapMenu();
+
+ assertNotNull("Tapping App info menu item returned null", menu.tapAppInfoMenuItem());
+ executeOnLauncher(launcher -> assertTrue(
+ "Launcher activity is the top activity; expecting another activity to be the top",
+ isInLaunchedApp(launcher)));
+ }
+
private int getCurrentOverviewPage(Launcher launcher) {
return launcher.<RecentsView>getOverviewPanel().getCurrentPage();
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 28e0cf2..9a3480b 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -886,6 +886,10 @@
updateIconSize(1f, res);
updateWorkspacePadding();
+ if (mIsResponsiveGrid) {
+ return 0;
+ }
+
// Check to see if the icons fit within the available height.
float usedHeight = getCellLayoutHeightSpecification();
final int maxHeight = getCellLayoutHeight();
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index bfbca65..d2ea7cc 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -386,8 +386,7 @@
* Gets the translation provider for workspace pages.
*/
public PageTranslationProvider getWorkspacePageTranslationProvider(Launcher launcher) {
- if (this != SPRING_LOADED
- || this != EDIT_MODE
+ if (!(this == SPRING_LOADED || this == EDIT_MODE)
|| !launcher.getDeviceProfile().isTwoPanels) {
return DEFAULT_PAGE_TRANSLATION_PROVIDER;
}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index cabcabc..1f68e11 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -395,12 +395,12 @@
// TODO(Block 33): Clean up flags
public static final BooleanFlag ENABLE_ALL_APPS_RV_PREINFLATION = getDebugFlag(288161355,
- "ENABLE_ALL_APPS_RV_PREINFLATION", ENABLED,
+ "ENABLE_ALL_APPS_RV_PREINFLATION", DISABLED,
"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", ENABLED,
+ "ALL_APPS_GONE_VISIBILITY", DISABLED,
"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/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index de10fc5..f69d299 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -24,8 +24,7 @@
import static com.android.launcher3.allapps.AllAppsTransitionController.REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS;
import static com.android.launcher3.util.ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
@@ -51,6 +50,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
+import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
@@ -82,15 +82,19 @@
protected static final float TRANSLATION_SHIFT_CLOSED = 1f;
protected static final float TRANSLATION_SHIFT_OPENED = 0f;
private static final float VIEW_NO_SCALE = 1f;
+ private static final int NO_DURATION = -1;
protected final T mActivityContext;
protected final SingleAxisSwipeDetector mSwipeDetector;
- protected final ObjectAnimator mOpenCloseAnimator;
+ protected @NonNull AnimatorSet mOpenCloseAnimator;
+ private final ObjectAnimator mTranslationShiftAnimator;
protected ViewGroup mContent;
protected final View mColorScrim;
- protected Interpolator mScrollInterpolator;
+
+ private Interpolator mScrollInterpolator;
+ private long mScrollDuration;
// range [0, 1], 0=> completely open, 1=> completely closed
protected float mTranslationShift = TRANSLATION_SHIFT_CLOSED;
@@ -104,8 +108,8 @@
protected final AnimatedFloat mSlideInViewScale =
new AnimatedFloat(this::onScaleProgressChanged, VIEW_NO_SCALE);
protected boolean mIsBackProgressing;
- @Nullable private Drawable mContentBackground;
- @Nullable private View mContentBackgroundParentView;
+ private @Nullable Drawable mContentBackground;
+ private @Nullable View mContentBackgroundParentView;
protected final ViewOutlineProvider mViewOutlineProvider = new ViewOutlineProvider() {
@Override
@@ -124,21 +128,52 @@
mActivityContext = ActivityContext.lookupContext(context);
mScrollInterpolator = Interpolators.SCROLL_CUBIC;
+ mScrollDuration = NO_DURATION;
mSwipeDetector = new SingleAxisSwipeDetector(context, this,
SingleAxisSwipeDetector.VERTICAL);
- mOpenCloseAnimator = ObjectAnimator.ofPropertyValuesHolder(this);
- mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mSwipeDetector.finishedScrolling();
- announceAccessibilityChanges();
- }
- });
+ mOpenCloseAnimator = new AnimatorSet();
+ mTranslationShiftAnimator = ObjectAnimator.ofPropertyValuesHolder(this);
+
int scrimColor = getScrimColor(context);
mColorScrim = scrimColor != -1 ? createColorScrim(context, scrimColor) : null;
}
+ /**
+ * Sets up a {@link #mOpenCloseAnimator} for opening with default parameters.
+ *
+ * @see #setUpOpenCloseAnimator(float, Interpolator)
+ */
+ protected final AnimatorSet setUpDefaultOpenAnimator() {
+ return setUpOpenCloseAnimator(TRANSLATION_SHIFT_OPENED, Interpolators.FAST_OUT_SLOW_IN);
+ }
+
+ /**
+ * Initializes a new {@link #mOpenCloseAnimator}.
+ * <p>
+ * Subclasses should override this method if they want to add more {@code Animator} instances
+ * to the set.
+ *
+ * @param translationShift translation shift to animate to.
+ * @param translationShiftInterpolator interpolator for {@link #mTranslationShiftAnimator}.
+ * @return {@link #mOpenCloseAnimator}
+ */
+ protected AnimatorSet setUpOpenCloseAnimator(
+ float translationShift, Interpolator translationShiftInterpolator) {
+ mOpenCloseAnimator = new AnimatorSet();
+ mOpenCloseAnimator.addListener(AnimatorListeners.forEndCallback(() -> {
+ mSwipeDetector.finishedScrolling();
+ announceAccessibilityChanges();
+ }));
+
+ mTranslationShiftAnimator.setValues(PropertyValuesHolder.ofFloat(
+ TRANSLATION_SHIFT, translationShift));
+ mTranslationShiftAnimator.setInterpolator(translationShiftInterpolator);
+ mOpenCloseAnimator.play(mTranslationShiftAnimator);
+
+ return mOpenCloseAnimator;
+ }
+
protected void attachToContainer() {
if (mColorScrim != null) {
getPopupContainer().addView(mColorScrim);
@@ -309,16 +344,13 @@
if ((mSwipeDetector.isFling(velocity) && velocity > 0)
|| mTranslationShift > successfulShiftThreshold) {
mScrollInterpolator = scrollInterpolatorForVelocity(velocity);
- mOpenCloseAnimator.setDuration(BaseSwipeDetector.calculateDuration(
- velocity, TRANSLATION_SHIFT_CLOSED - mTranslationShift));
+ mScrollDuration = BaseSwipeDetector.calculateDuration(
+ velocity, TRANSLATION_SHIFT_CLOSED - mTranslationShift);
close(true);
} else {
- mOpenCloseAnimator.setValues(PropertyValuesHolder.ofFloat(
- TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
- mOpenCloseAnimator.setDuration(
- BaseSwipeDetector.calculateDuration(velocity, mTranslationShift))
- .setInterpolator(Interpolators.DECELERATE);
- mOpenCloseAnimator.start();
+ setUpOpenCloseAnimator(TRANSLATION_SHIFT_OPENED, Interpolators.DECELERATE)
+ .setDuration(BaseSwipeDetector.calculateDuration(velocity, mTranslationShift))
+ .start();
}
}
@@ -344,23 +376,19 @@
onCloseComplete();
return;
}
- mOpenCloseAnimator.setValues(
- PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_CLOSED));
- mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mOpenCloseAnimator.removeListener(this);
- onCloseComplete();
- }
- });
+
+ final Interpolator interpolator;
+ final long duration;
if (mSwipeDetector.isIdleState()) {
- mOpenCloseAnimator
- .setDuration(defaultDuration)
- .setInterpolator(getIdleInterpolator());
+ interpolator = getIdleInterpolator();
+ duration = defaultDuration;
} else {
- mOpenCloseAnimator.setInterpolator(mScrollInterpolator);
+ interpolator = mScrollInterpolator;
+ duration = mScrollDuration > NO_DURATION ? mScrollDuration : defaultDuration;
}
- mOpenCloseAnimator.start();
+ setUpOpenCloseAnimator(TRANSLATION_SHIFT_CLOSED, interpolator)
+ .addListener(AnimatorListeners.forEndCallback(this::onCloseComplete));
+ mOpenCloseAnimator.setDuration(duration).start();
}
protected Interpolator getIdleInterpolator() {
diff --git a/src/com/android/launcher3/views/WidgetsEduView.java b/src/com/android/launcher3/views/WidgetsEduView.java
index 9180781..92e048b 100644
--- a/src/com/android/launcher3/views/WidgetsEduView.java
+++ b/src/com/android/launcher3/views/WidgetsEduView.java
@@ -15,9 +15,6 @@
*/
package com.android.launcher3.views;
-import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
-
-import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
@@ -123,10 +120,7 @@
return;
}
mIsOpen = true;
- mOpenCloseAnimator.setValues(
- PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
- mOpenCloseAnimator.setInterpolator(FAST_OUT_SLOW_IN);
- mOpenCloseAnimator.start();
+ setUpDefaultOpenAnimator().start();
}
/** Shows widget education dialog. */
diff --git a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
index 473abf1..c6fc5fe 100644
--- a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
@@ -16,10 +16,8 @@
package com.android.launcher3.widget;
-import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.Utilities.ATLEAST_R;
-import android.animation.PropertyValuesHolder;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Insets;
@@ -134,10 +132,7 @@
return;
}
mIsOpen = true;
- mOpenCloseAnimator.setValues(
- PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
- mOpenCloseAnimator.setInterpolator(FAST_OUT_SLOW_IN);
- mOpenCloseAnimator.start();
+ setUpDefaultOpenAnimator().start();
}
@Override
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 93f7cb3..82394f1 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -16,10 +16,8 @@
package com.android.launcher3.widget;
-import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_BOTTOM_WIDGETS_TRAY;
-import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
@@ -231,10 +229,7 @@
}
mIsOpen = true;
setupNavBarColor();
- mOpenCloseAnimator.setValues(
- PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
- mOpenCloseAnimator.setInterpolator(FAST_OUT_SLOW_IN);
- mOpenCloseAnimator.start();
+ setUpDefaultOpenAnimator().start();
}
@Override
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index c9cd4b6..43f1846 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -22,9 +22,6 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED;
import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.content.pm.LauncherApps;
import android.content.res.Configuration;
@@ -627,20 +624,14 @@
mContent.setAlpha(0);
setTranslationShift(VERTICAL_START_POSITION);
}
- mOpenCloseAnimator.setValues(
- PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
- mOpenCloseAnimator
- .setDuration(mActivityContext.getDeviceProfile().bottomSheetOpenDuration)
- .setInterpolator(AnimationUtils.loadInterpolator(
+ setUpOpenCloseAnimator(
+ TRANSLATION_SHIFT_OPENED,
+ AnimationUtils.loadInterpolator(
getContext(), android.R.interpolator.linear_out_slow_in));
- mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mOpenCloseAnimator.removeListener(this);
- }
- });
post(() -> {
- mOpenCloseAnimator.start();
+ mOpenCloseAnimator
+ .setDuration(mActivityContext.getDeviceProfile().bottomSheetOpenDuration)
+ .start();
mContent.animate().alpha(1).setDuration(FADE_IN_DURATION);
});
} else {
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 28688fd..bb61fbe 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -32,6 +32,7 @@
<receiver
android:name="com.android.launcher3.testcomponent.AppWidgetNoConfig"
android:exported="true"
+ android:icon="@drawable/test_widget_no_config_icon"
android:label="No Config">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
@@ -65,6 +66,7 @@
<receiver
android:name="com.android.launcher3.testcomponent.AppWidgetWithConfig"
android:exported="true"
+ android:icon="@drawable/test_widget_with_config_icon"
android:label="With Config">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
@@ -76,6 +78,7 @@
<receiver
android:name="com.android.launcher3.testcomponent.AppWidgetWithDialog"
android:exported="true"
+ android:icon="@drawable/test_widget_with_dialog_icon"
android:label="With Dialog">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
@@ -87,6 +90,7 @@
<receiver
android:name="com.android.launcher3.testcomponent.AppWidgetDynamicColors"
android:exported="true"
+ android:icon="@drawable/test_widget_dynamic_colors_icon"
android:label="Dynamic Colors">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
diff --git a/tests/res/drawable/test_widget_dynamic_colors_icon.xml b/tests/res/drawable/test_widget_dynamic_colors_icon.xml
new file mode 100644
index 0000000..69f6675
--- /dev/null
+++ b/tests/res/drawable/test_widget_dynamic_colors_icon.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+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.
+-->
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@android:color/white"/>
+ <foreground>
+ <color android:color="#964B00"/>
+ </foreground>
+ <monochrome>
+ <vector android:width="48dp" android:height="48dp" android:viewportWidth="48.0" android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M0,24L48,24 48,48, 0,48 Z"/>
+ </vector>
+ </monochrome>
+</adaptive-icon>
diff --git a/tests/res/drawable/test_widget_no_config_icon.xml b/tests/res/drawable/test_widget_no_config_icon.xml
new file mode 100644
index 0000000..e3d0125
--- /dev/null
+++ b/tests/res/drawable/test_widget_no_config_icon.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+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.
+-->
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@android:color/white"/>
+ <foreground>
+ <color android:color="#00FFFF"/>
+ </foreground>
+ <monochrome>
+ <vector android:width="48dp" android:height="48dp" android:viewportWidth="48.0" android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M0,24L48,24 48,48, 0,48 Z"/>
+ </vector>
+ </monochrome>
+</adaptive-icon>
diff --git a/tests/res/drawable/test_widget_with_config_icon.xml b/tests/res/drawable/test_widget_with_config_icon.xml
new file mode 100644
index 0000000..98b797b
--- /dev/null
+++ b/tests/res/drawable/test_widget_with_config_icon.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+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.
+-->
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@android:color/white"/>
+ <foreground>
+ <color android:color="#008000" />
+ </foreground>
+ <monochrome>
+ <vector android:width="48dp" android:height="48dp" android:viewportWidth="48.0" android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M0,24L48,24 48,48, 0,48 Z"/>
+ </vector>
+ </monochrome>
+</adaptive-icon>
diff --git a/tests/res/drawable/test_widget_with_dialog_icon.xml b/tests/res/drawable/test_widget_with_dialog_icon.xml
new file mode 100644
index 0000000..d2879d2
--- /dev/null
+++ b/tests/res/drawable/test_widget_with_dialog_icon.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+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.
+-->
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@android:color/white"/>
+ <foreground>
+ <color android:color="#800080"/>
+ </foreground>
+ <monochrome>
+ <vector android:width="48dp" android:height="48dp" android:viewportWidth="48.0" android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M0,24L48,24 48,48, 0,48 Z"/>
+ </vector>
+ </monochrome>
+</adaptive-icon>
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index b67478f..753d89d 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -318,6 +318,7 @@
}
@Test
+ @Ignore // b/293191790
@PortraitLandscape
public void testWidgets() throws Exception {
// Test opening widgets.
diff --git a/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt b/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt
index b4ad1f3..f3f9b89 100644
--- a/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt
+++ b/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt
@@ -26,8 +26,13 @@
import com.android.launcher3.tapl.TestHelpers
import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter
import com.android.launcher3.util.viewcapture_analysis.ViewCaptureAnalyzer
-import org.junit.Assert.assertTrue
+import java.io.BufferedOutputStream
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.OutputStreamWriter
import java.util.function.Supplier
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
@@ -81,7 +86,7 @@
MAIN_EXECUTOR.execute { windowListenerCloseables.onEach(SafeCloseable::close) }
}
- analyzeViewCapture()
+ analyzeViewCapture(description)
}
private fun startCapturingExistingActivity(
@@ -107,16 +112,39 @@
}
}
- private fun analyzeViewCapture() {
+ private fun analyzeViewCapture(description: Description) {
// OOP tests don't produce ViewCapture data
if (!TestHelpers.isInLauncherProcess()) return
- ViewCaptureAnalyzer.assertNoAnomalies(viewCaptureData)
-
var frameCount = 0
for (i in 0 until viewCaptureData!!.windowDataCount) {
frameCount += viewCaptureData!!.getWindowData(i).frameDataCount
}
assertTrue("Empty ViewCapture data", frameCount > 0)
+
+ val anomalies: Map<String, String> = ViewCaptureAnalyzer.getAnomalies(viewCaptureData)
+ if (!anomalies.isEmpty()) {
+ val diagFile = FailureWatcher.diagFile(description, "ViewAnomalies", "txt")
+ try {
+ OutputStreamWriter(BufferedOutputStream(FileOutputStream(diagFile))).use { writer ->
+ writer.write("View animation anomalies detected.\r\n")
+ writer.write(
+ "To suppress an anomaly for a view, add its full path to the PATHS_TO_IGNORE list in the corresponding AnomalyDetector.\r\n"
+ )
+ writer.write("List of views with animation anomalies:\r\n")
+
+ for ((viewPath, message) in anomalies) {
+ writer.write("View: $viewPath\r\n $message\r\n")
+ }
+ }
+ } catch (ex: IOException) {
+ throw RuntimeException(ex)
+ }
+
+ val (viewPath, message) = anomalies.entries.first()
+ fail(
+ "${anomalies.size} view(s) had animation anomalies during the test, including view: $viewPath: $message\r\nSee ${diagFile.name} for details."
+ )
+ }
}
}
diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java
index cb404b9..a147350 100644
--- a/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java
+++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java
@@ -113,7 +113,7 @@
DRAG_LAYER + "WidgetsFullSheet|SpringRelativeLayout:id/container",
DRAG_LAYER + "WidgetsTwoPaneSheet|SpringRelativeLayout:id/container",
CONTENT + "LauncherRootView:id/launcher|FloatingIconView",
- RECENTS_DRAG_LAYER + "ArrowTipView|View:id/arrow",
+ RECENTS_DRAG_LAYER + "ArrowTipView",
DRAG_LAYER + "FallbackRecentsView:id/overview_panel",
RECENTS_DRAG_LAYER + "FallbackRecentsView:id/overview_panel",
DRAG_LAYER
@@ -133,7 +133,8 @@
+ "|LinearLayout:id/action_buttons|ImageButton:id/action_split",
RECENTS_DRAG_LAYER
+ "NexusOverviewActionsView:id/overview_actions_view"
- + "|LinearLayout:id/action_buttons|ImageButton:id/action_split"
+ + "|LinearLayout:id/action_buttons|ImageButton:id/action_split",
+ DRAG_LAYER + "IconView"
);
/**
@@ -212,24 +213,26 @@
}
@Override
- void detectAnomalies(AnalysisNode oldInfo, AnalysisNode newInfo, int frameN) {
+ String detectAnomalies(AnalysisNode oldInfo, AnalysisNode newInfo, int frameN) {
// If the view was previously seen, proceed with analysis only if it was present in the
// view hierarchy in the previous frame.
- if (oldInfo != null && oldInfo.frameN != frameN) return;
+ if (oldInfo != null && oldInfo.frameN != frameN) return null;
final AnalysisNode latestInfo = newInfo != null ? newInfo : oldInfo;
- if (getNodeData(latestInfo).ignoreAlphaJumps) return;
+ final NodeData nodeData = getNodeData(latestInfo);
+ if (nodeData.ignoreAlphaJumps) return null;
final float oldAlpha = oldInfo != null ? oldInfo.alpha : 0;
final float newAlpha = newInfo != null ? newInfo.alpha : 0;
final float alphaDeltaAbs = Math.abs(newAlpha - oldAlpha);
if (alphaDeltaAbs >= ALPHA_JUMP_THRESHOLD) {
- throw new AssertionError(
- String.format(
- "Alpha jump detected in ViewCapture data: alpha change: %s (%s -> %s)"
- + ", threshold: %s, view: %s",
- alphaDeltaAbs, oldAlpha, newAlpha, ALPHA_JUMP_THRESHOLD, latestInfo));
+ nodeData.ignoreAlphaJumps = true; // No need to report alpha jump in children.
+ return String.format(
+ "Alpha jump detected in ViewCapture data: alpha change: %s (%s -> %s)"
+ + ", threshold: %s, %s", // ----------- no need to include view?
+ alphaDeltaAbs, oldAlpha, newAlpha, ALPHA_JUMP_THRESHOLD, latestInfo);
}
+ return null;
}
}
diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java
index 2cf3843..949c536 100644
--- a/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java
+++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java
@@ -61,8 +61,9 @@
* the view is not present in the 'currentFrame', but was present in earlier
* frames.
* @param frameN number of the current frame.
+ * @return Anomaly diagnostic message if an anomaly has been detected; null otherwise.
*/
- abstract void detectAnomalies(
+ abstract String detectAnomalies(
@Nullable AnalysisNode oldInfo, @Nullable AnalysisNode newInfo, int frameN);
}
@@ -101,25 +102,31 @@
@Override
public String toString() {
- return String.format("window coordinates: (%s, %s), class path from the root: %s",
- left, top, diagPathFromRoot(this));
+ return String.format("view window coordinates: (%s, %s)", left, top);
}
}
/**
- * Scans a view capture record and throws an error if an anomaly is found.
+ * Scans a view capture record and searches for view animation anomalies. Can find anomalies for
+ * multiple views.
+ * Returns a map from the view path to the anomaly message for the view. Non-empty map means
+ * that anomalies were detected.
*/
- public static void assertNoAnomalies(ExportedData viewCaptureData) {
+ public static Map<String, String> getAnomalies(ExportedData viewCaptureData) {
+ final Map<String, String> anomalies = new HashMap<>();
+
final int scrimClassIndex = viewCaptureData.getClassnameList().indexOf(SCRIM_VIEW_CLASS);
final int windowDataCount = viewCaptureData.getWindowDataCount();
for (int i = 0; i < windowDataCount; ++i) {
- analyzeWindowData(viewCaptureData, viewCaptureData.getWindowData(i), scrimClassIndex);
+ analyzeWindowData(
+ viewCaptureData, viewCaptureData.getWindowData(i), scrimClassIndex, anomalies);
}
+ return anomalies;
}
private static void analyzeWindowData(ExportedData viewCaptureData, WindowData windowData,
- int scrimClassIndex) {
+ int scrimClassIndex, Map<String, String> anomalies) {
// View hash code => Last seen node with this hash code.
// The view is added when we analyze the first frame where it's visible.
// After that, it gets updated for every frame where it's visible.
@@ -128,12 +135,13 @@
for (int frameN = 0; frameN < windowData.getFrameDataCount(); ++frameN) {
analyzeFrame(frameN, windowData.getFrameData(frameN), viewCaptureData, lastSeenNodes,
- scrimClassIndex);
+ scrimClassIndex, anomalies);
}
}
private static void analyzeFrame(int frameN, FrameData frame, ExportedData viewCaptureData,
- Map<Integer, AnalysisNode> lastSeenNodes, int scrimClassIndex) {
+ Map<Integer, AnalysisNode> lastSeenNodes, int scrimClassIndex,
+ Map<String, String> anomalies) {
// Analyze the node tree starting from the root.
analyzeView(
frame.getNode(),
@@ -143,7 +151,8 @@
/* topShift = */ 0,
viewCaptureData,
lastSeenNodes,
- scrimClassIndex);
+ scrimClassIndex,
+ anomalies);
// Analyze transitions when a view visible in the last frame become invisible in the
// current one.
@@ -151,10 +160,14 @@
if (info.frameN == frameN - 1) {
if (!info.viewCaptureNode.getWillNotDraw()) {
Arrays.stream(ANOMALY_DETECTORS).forEach(
- detector -> detector.detectAnomalies(
- /* oldInfo = */ info,
- /* newInfo = */ null,
- frameN));
+ detector ->
+ detectAnomaly(
+ detector,
+ frameN,
+ /* oldInfo = */ info,
+ /* newInfo = */ null,
+ anomalies)
+ );
}
}
}
@@ -162,7 +175,8 @@
private static void analyzeView(ViewNode viewCaptureNode, AnalysisNode parent, int frameN,
float leftShift, float topShift, ExportedData viewCaptureData,
- Map<Integer, AnalysisNode> lastSeenNodes, int scrimClassIndex) {
+ Map<Integer, AnalysisNode> lastSeenNodes, int scrimClassIndex,
+ Map<String, String> anomalies) {
// Skip analysis of invisible views
final float parentAlpha = parent != null ? parent.alpha : 1;
final float alpha = getVisibleAlpha(viewCaptureNode, parentAlpha);
@@ -205,7 +219,10 @@
final AnalysisNode oldAnalysisNode = lastSeenNodes.get(hashcode); // may be null
if (frameN != 0 && !viewCaptureNode.getWillNotDraw()) {
Arrays.stream(ANOMALY_DETECTORS).forEach(
- detector -> detector.detectAnomalies(oldAnalysisNode, newAnalysisNode, frameN));
+ detector ->
+ detectAnomaly(detector, frameN, oldAnalysisNode, newAnalysisNode,
+ anomalies)
+ );
}
lastSeenNodes.put(hashcode, newAnalysisNode);
@@ -220,8 +237,20 @@
if (child.getClassnameIndex() == scrimClassIndex) break;
analyzeView(child, newAnalysisNode, frameN, leftShiftForChildren, topShiftForChildren,
- viewCaptureData, lastSeenNodes,
- scrimClassIndex);
+ viewCaptureData, lastSeenNodes, scrimClassIndex, anomalies);
+ }
+ }
+
+ private static void detectAnomaly(AnomalyDetector detector, int frameN,
+ AnalysisNode oldAnalysisNode, AnalysisNode newAnalysisNode,
+ Map<String, String> anomalies) {
+ final String maybeAnomaly =
+ detector.detectAnomalies(oldAnalysisNode, newAnalysisNode, frameN);
+ if (maybeAnomaly != null) {
+ final String viewDiagPath = diagPathFromRoot(newAnalysisNode);
+ if (!anomalies.containsKey(viewDiagPath)) {
+ anomalies.put(viewDiagPath, maybeAnomaly);
+ }
}
}
@@ -235,9 +264,11 @@
return className.substring(className.lastIndexOf(".") + 1);
}
- private static String diagPathFromRoot(AnalysisNode nodeBox) {
- final StringBuilder path = new StringBuilder(nodeBox.nodeIdentity);
- for (AnalysisNode ancestor = nodeBox.parent; ancestor != null; ancestor = ancestor.parent) {
+ private static String diagPathFromRoot(AnalysisNode analysisNode) {
+ final StringBuilder path = new StringBuilder(analysisNode.nodeIdentity);
+ for (AnalysisNode ancestor = analysisNode.parent;
+ ancestor != null;
+ ancestor = ancestor.parent) {
path.insert(0, ancestor.nodeIdentity + "|");
}
return path.toString();
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
index e349620..7c29a6c 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
@@ -50,6 +50,24 @@
}
}
+ /** Taps the app info item from the overview task menu and returns the LaunchedAppState
+ * representing the App info settings page. */
+ @NonNull
+ public LaunchedAppState tapAppInfoMenuItem() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "before tapping the app info menu item")) {
+ mLauncher.clickLauncherObject(
+ mLauncher.findObjectInContainer(mMenu, By.text("App info")));
+
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "tapped app info menu item")) {
+ mLauncher.waitUntilSystemLauncherObjectGone("overview_panel");
+ return new LaunchedAppState(mLauncher);
+ }
+ }
+ }
+
/** Returns true if an item matching the given string is present in the menu. */
public boolean hasMenuItem(String expectedMenuItemText) {
UiObject2 menuItem = mLauncher.findObjectInContainer(mMenu, By.text(expectedMenuItemText));