Merge "Fix for recents button quick switch with only focus and desktop task" into main
diff --git a/go/quickstep/src/com/android/launcher3/AppSharing.java b/go/quickstep/src/com/android/launcher3/AppSharing.java
index e15b132..a97fecc 100644
--- a/go/quickstep/src/com/android/launcher3/AppSharing.java
+++ b/go/quickstep/src/com/android/launcher3/AppSharing.java
@@ -37,12 +37,13 @@
import androidx.core.content.FileProvider;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.AppShareabilityChecker;
import com.android.launcher3.model.AppShareabilityJobService;
import com.android.launcher3.model.AppShareabilityManager;
import com.android.launcher3.model.AppShareabilityManager.ShareabilityStatus;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.views.ActivityContext;
@@ -114,17 +115,17 @@
* The Share App system shortcut, used to initiate p2p sharing of a given app
*/
public final class Share extends SystemShortcut<Launcher> {
- private final PopupDataProvider mPopupDataProvider;
private final boolean mSharingEnabledForUser;
private final Set<View> mBoundViews = Collections.newSetFromMap(new WeakHashMap<>());
private boolean mIsEnabled = true;
+ private StatsLogManager mStatsLogManager;
public Share(Launcher target, ItemInfo itemInfo, View originalView) {
super(R.drawable.ic_share, R.string.app_share_drop_target_label, target, itemInfo,
originalView);
- mPopupDataProvider = target.getPopupDataProvider();
-
+ mStatsLogManager = ActivityContext.lookupContext(originalView.getContext())
+ .getStatsLogManager();
mSharingEnabledForUser = bluetoothSharingEnabled(target);
if (!mSharingEnabledForUser) {
setEnabled(false);
@@ -150,8 +151,7 @@
@Override
public void onClick(View view) {
- ActivityContext.lookupContext(view.getContext())
- .getStatsLogManager().logger().log(LAUNCHER_SYSTEM_SHORTCUT_APP_SHARE_TAP);
+ mStatsLogManager.logger().log(LAUNCHER_SYSTEM_SHORTCUT_APP_SHARE_TAP);
if (!mIsEnabled) {
showCannotShareToast(view.getContext());
return;
@@ -240,6 +240,11 @@
public boolean isEnabled() {
return mIsEnabled;
}
+
+ @VisibleForTesting
+ void setStatsLogManager(StatsLogManager statsLogManager) {
+ mStatsLogManager = statsLogManager;
+ }
}
/**
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index a74a17b..04b04dd 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -139,8 +139,7 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"Always show Taskbar"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"Change navigation mode"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"Taskbar divider"</string>
- <!-- no translation found for taskbar_overflow_a11y_title (7960342079198820179) -->
- <skip />
+ <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"Taskbar overflow"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{more app}other{more apps}}"</string>
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
index f7da34a..8b3a032 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
@@ -20,6 +20,7 @@
import android.os.RemoteException
import android.util.Log
import android.view.SurfaceControl
+import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.IRemoteTransitionFinishedCallback
import android.window.RemoteTransition
import android.window.RemoteTransitionStub
@@ -30,6 +31,7 @@
import com.android.quickstep.SystemUiProxy
import com.android.quickstep.TaskViewUtils
import com.android.quickstep.views.DesktopTaskView
+import com.android.window.flags.Flags
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
import java.util.function.Consumer
@@ -38,14 +40,14 @@
private val stateManager: StateManager<*, *>,
private val systemUiProxy: SystemUiProxy,
private val appThread: IApplicationThread,
- private val depthController: DepthController?
+ private val depthController: DepthController?,
) {
/** Launch desktop tasks from recents view */
fun launchDesktopFromRecents(
desktopTaskView: DesktopTaskView,
animated: Boolean,
- callback: Consumer<Boolean>? = null
+ callback: Consumer<Boolean>? = null,
) {
val animRunner =
RemoteDesktopLaunchTransitionRunner(
@@ -53,7 +55,7 @@
animated,
stateManager,
depthController,
- callback
+ callback,
)
val transition = RemoteTransition(animRunner, appThread, "RecentsToDesktop")
systemUiProxy.showDesktopApps(desktopTaskView.display.displayId, transition)
@@ -69,14 +71,14 @@
private val animated: Boolean,
private val stateManager: StateManager<*, *>,
private val depthController: DepthController?,
- private val successCallback: Consumer<Boolean>?
+ private val successCallback: Consumer<Boolean>?,
) : RemoteTransitionStub() {
override fun startAnimation(
token: IBinder,
info: TransitionInfo,
t: SurfaceControl.Transaction,
- finishCallback: IRemoteTransitionFinishedCallback
+ finishCallback: IRemoteTransitionFinishedCallback,
) {
val errorHandlingFinishCallback = Runnable {
try {
@@ -86,6 +88,9 @@
}
}
+ if (Flags.enableDesktopWindowingPersistence()) {
+ handleAnimationAfterReboot(info)
+ }
MAIN_EXECUTOR.execute {
val animator =
TaskViewUtils.composeRecentsDesktopLaunchAnimator(
@@ -93,7 +98,7 @@
stateManager,
depthController,
info,
- t
+ t,
) {
errorHandlingFinishCallback.run()
successCallback?.accept(true)
@@ -104,6 +109,26 @@
animator.start()
}
}
+
+ /**
+ * Upon reboot the start bounds of a task is set to fullscreen with the recents transition.
+ * Check this case and set the start bounds to the end bounds so that the window doesn't
+ * jump from start bounds to end bounds during the animation. Tasks in desktop cannot
+ * normally have top bound as 0 due to status bar so this is a good indicator to identify
+ * reboot case.
+ */
+ private fun handleAnimationAfterReboot(info: TransitionInfo) {
+ info.changes.forEach { change ->
+ if (
+ change.mode == TRANSIT_TO_FRONT &&
+ change.taskInfo?.isFreeform == true &&
+ change.startAbsBounds.top == 0 &&
+ change.startAbsBounds.left == 0
+ ) {
+ change.setStartAbsBounds(change.endAbsBounds)
+ }
+ }
+ }
}
companion object {
diff --git a/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
index 212a5ff..4293ccd 100644
--- a/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
+++ b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
@@ -61,6 +61,7 @@
}
} catch (NullPointerException | ActivityNotFoundException | SecurityException
| SendIntentException e) {
+ Log.w(TAG, "Proxy activity starter could not start activity: ", e);
mParams.deliverResult(this, RESULT_CANCELED, null);
}
finishAndRemoveTask();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 065646a..8f722f4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -24,6 +24,7 @@
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_OVERVIEW;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_FOR_BUBBLES;
+import static com.android.launcher3.taskbar.TaskbarStashController.UNLOCK_TRANSITION_MEMOIZATION_MS;
import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
import static com.android.launcher3.taskbar.bubbles.BubbleBarView.FADE_IN_ANIM_ALPHA_DURATION_MS;
import static com.android.launcher3.taskbar.bubbles.BubbleBarView.FADE_OUT_ANIM_POSITION_DURATION_MS;
@@ -57,6 +58,7 @@
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.BubbleLauncherState;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
@@ -167,7 +169,12 @@
private boolean mSkipNextRecentsAnimEnd;
// Time when FLAG_TASKBAR_HIDDEN was last cleared, SystemClock.elapsedRealtime (milliseconds).
- private long mLastUnlockTimeMs = 0;
+ private long mLastRemoveTaskbarHiddenTimeMs = 0;
+ /**
+ * Time when FLAG_DEVICE_LOCKED was last cleared, plus
+ * {@link TaskbarStashController#UNLOCK_TRANSITION_MEMOIZATION_MS}
+ */
+ private long mLastUnlockTransitionTimeout;
private @Nullable TaskBarRecentsAnimationListener mTaskBarRecentsAnimationListener;
@@ -473,8 +480,12 @@
boolean onOverview = mLauncherState == LauncherState.OVERVIEW;
boolean hotseatIconsVisible = isInLauncher && mLauncherState.areElementsVisible(
mLauncher, HOTSEAT_ICONS);
- controllers.bubbleStashController.setBubblesShowingOnHome(hotseatIconsVisible);
- controllers.bubbleStashController.setBubblesShowingOnOverview(onOverview);
+ BubbleLauncherState state = onOverview
+ ? BubbleLauncherState.OVERVIEW
+ : hotseatIconsVisible
+ ? BubbleLauncherState.HOME
+ : BubbleLauncherState.IN_APP;
+ controllers.bubbleStashController.setLauncherState(state);
});
TaskbarStashController stashController = mControllers.taskbarStashController;
@@ -535,7 +546,7 @@
if (hasAnyFlag(changedFlags, FLAG_TASKBAR_HIDDEN) && !hasAnyFlag(FLAG_TASKBAR_HIDDEN)) {
// Take note of the current time, as the taskbar is made visible again.
- mLastUnlockTimeMs = SystemClock.elapsedRealtime();
+ mLastRemoveTaskbarHiddenTimeMs = SystemClock.elapsedRealtime();
}
boolean isHidden = hasAnyFlag(FLAG_TASKBAR_HIDDEN);
@@ -561,7 +572,8 @@
// with a fingerprint reader. This should only be done when the device was woken
// up via fingerprint reader, however since this information is currently not
// available, opting to always delay the fade-in a bit.
- long durationSinceLastUnlockMs = SystemClock.elapsedRealtime() - mLastUnlockTimeMs;
+ long durationSinceLastUnlockMs = SystemClock.elapsedRealtime()
+ - mLastRemoveTaskbarHiddenTimeMs;
taskbarVisibility.setStartDelay(
Math.max(0, TASKBAR_SHOW_DELAY_MS - durationSinceLastUnlockMs));
}
@@ -631,6 +643,15 @@
boolean isUnlockTransition =
hasAnyFlag(changedFlags, FLAG_DEVICE_LOCKED) && !hasAnyFlag(FLAG_DEVICE_LOCKED);
if (isUnlockTransition) {
+ // the launcher might not be resumed at the time the device is considered
+ // unlocked (when the keyguard goes away), but possibly shortly afterwards.
+ // To play the unlock transition at the time the unstash animation actually happens,
+ // this memoizes the state transition for UNLOCK_TRANSITION_MEMOIZATION_MS.
+ mLastUnlockTransitionTimeout =
+ SystemClock.elapsedRealtime() + UNLOCK_TRANSITION_MEMOIZATION_MS;
+ }
+ boolean isInUnlockTimeout = SystemClock.elapsedRealtime() < mLastUnlockTransitionTimeout;
+ if (isUnlockTransition || isInUnlockTimeout) {
// When transitioning to unlocked, ensure the hotseat is fully visible from the
// beginning. The hotseat itself is animated by LauncherUnlockAnimationController.
mIconAlignment.cancelAnimation();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 6222e53..8991965 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -191,7 +191,7 @@
// Duration for which an unlock event is considered "current", as other events are received
// asynchronously.
- private static final long UNLOCK_TRANSITION_MEMOIZATION_MS = 200;
+ public static final long UNLOCK_TRANSITION_MEMOIZATION_MS = 200;
/**
* The default stash animation, morphing the taskbar into the navbar.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index b495e7d..e522035 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -329,7 +329,8 @@
*/
public void setRecentsButtonDisabled(boolean isDisabled) {
// TODO: check TaskbarStashController#supportsStashing(), to stash instead of setting alpha.
- mTaskbarIconAlpha.get(ALPHA_INDEX_RECENTS_DISABLED).setValue(isDisabled ? 0 : 1);
+ mTaskbarIconAlpha.get(ALPHA_INDEX_RECENTS_DISABLED).animateToValue(isDisabled ? 0 : 1)
+ .start();
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
index f08318e..249773d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
@@ -43,9 +43,10 @@
private val arrowTipRadius: Float
private val arrowVisibleHeight: Float
- private val shadowAlpha: Float
- private var shadowBlur = 0f
- private var keyShadowDistance = 0f
+ private val strokeAlpha: Int
+ private val shadowAlpha: Int
+ private val shadowBlur: Float
+ private val keyShadowDistance: Float
private var arrowHeightFraction = 1f
var arrowPositionX: Float = 0f
@@ -105,13 +106,13 @@
strokePaint.strokeWidth = res.getDimension(R.dimen.transient_taskbar_stroke_width)
// apply theme alpha attributes
if (Utilities.isDarkTheme(context)) {
- strokePaint.alpha = DARK_THEME_STROKE_ALPHA
+ strokeAlpha = DARK_THEME_STROKE_ALPHA
shadowAlpha = DARK_THEME_SHADOW_ALPHA
} else {
- strokePaint.alpha = LIGHT_THEME_STROKE_ALPHA
+ strokeAlpha = LIGHT_THEME_STROKE_ALPHA
shadowAlpha = LIGHT_THEME_SHADOW_ALPHA
}
-
+ strokePaint.alpha = strokeAlpha
shadowBlur = res.getDimension(R.dimen.transient_taskbar_shadow_blur)
keyShadowDistance = res.getDimension(R.dimen.transient_taskbar_key_shadow_distance)
arrowWidth = res.getDimension(R.dimen.bubblebar_pointer_width)
@@ -132,15 +133,14 @@
override fun draw(canvas: Canvas) {
canvas.save()
- // TODO (b/277359345): Should animate the alpha similar to taskbar (see TaskbarDragLayer)
// Draw shadows.
val newShadowAlpha =
- mapToRange(fillPaint.alpha.toFloat(), 0f, 255f, 0f, shadowAlpha, Interpolators.LINEAR)
+ mapToRange(fillPaint.alpha, 0, 255, 0, shadowAlpha, Interpolators.LINEAR)
fillPaint.setShadowLayer(
shadowBlur,
0f,
keyShadowDistance,
- setColorAlphaBound(Color.BLACK, Math.round(newShadowAlpha))
+ setColorAlphaBound(Color.BLACK, newShadowAlpha),
)
// Create background path
val backgroundPath = Path()
@@ -172,7 +172,7 @@
arrowWidth,
scaledHeight,
arrowTipRadius,
- arrowPath
+ arrowPath,
)
// flip it horizontally
val pathTransform = Matrix()
@@ -196,6 +196,7 @@
override fun setAlpha(alpha: Int) {
fillPaint.alpha = alpha
+ strokePaint.alpha = mapToRange(alpha, 0, 255, 0, strokeAlpha, Interpolators.LINEAR)
invalidateSelf()
}
@@ -237,7 +238,7 @@
companion object {
private const val DARK_THEME_STROKE_ALPHA = 51
private const val LIGHT_THEME_STROKE_ALPHA = 41
- private const val DARK_THEME_SHADOW_ALPHA = 51f
- private const val LIGHT_THEME_SHADOW_ALPHA = 25f
+ private const val DARK_THEME_SHADOW_ALPHA = 51
+ private const val LIGHT_THEME_SHADOW_ALPHA = 25
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 025c038..ba180a6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -675,7 +675,8 @@
// if the animation is suppressed, immediately stash or show the bubble bar to
// ensure they've been initialized.
if (mTaskbarStashController.isInApp()
- && mBubbleStashController.isTransientTaskBar()) {
+ && mBubbleStashController.isTransientTaskBar()
+ && mTaskbarStashController.isStashed()) {
mBubbleStashController.stashBubbleBarImmediate();
} else {
mBubbleStashController.showBubbleBarImmediate();
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutPositioner.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutPositioner.kt
index b8c1c7f..aa2555e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutPositioner.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutPositioner.kt
@@ -39,4 +39,13 @@
/** The color of the flyout when collapsed. */
val collapsedColor: Int
+
+ /** The elevation of the flyout when collapsed. */
+ val collapsedElevation: Float
+
+ /**
+ * The distance the flyout must pass from its collapsed position until it can start revealing
+ * the triangle.
+ */
+ val distanceToRevealTriangle: Float
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
index 6723f57..8d84ddf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
@@ -40,8 +40,6 @@
ConstraintLayout(context) {
private companion object {
- // the minimum progress of the expansion animation before the triangle is made visible.
- const val MIN_EXPANSION_PROGRESS_FOR_TRIANGLE = 0.1f
// the minimum progress of the expansion animation before the content starts fading in.
const val MIN_EXPANSION_PROGRESS_FOR_CONTENT_ALPHA = 0.75f
}
@@ -92,6 +90,11 @@
context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_max_width)
}
+ private val flyoutElevation by
+ lazy(LazyThreadSafetyMode.NONE) {
+ context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_elevation).toFloat()
+ }
+
/** The bounds of the background rect. */
private val backgroundRect = RectF()
private val cornerRadius: Float
@@ -108,6 +111,10 @@
private var collapsedCornerRadius = 0f
/** The color of the flyout when collapsed. */
private var collapsedColor = 0
+ /** The elevation of the flyout when collapsed. */
+ private var collapsedElevation = 0f
+ /** The minimum progress of the expansion animation before the triangle is made visible. */
+ private var minExpansionProgressForTriangle = 0f
/** The corner radius of the background according to the progress of the animation. */
private val currentCornerRadius
@@ -141,8 +148,7 @@
val padding = context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_padding)
// add extra padding to the bottom of the view to include the triangle
setPadding(padding, padding, padding, padding + triangleHeight - triangleOverlap)
- translationZ =
- context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_elevation).toFloat()
+ translationZ = flyoutElevation
RoundedArrowDrawable.addDownPointingRoundedTriangleToPath(
triangleWidth.toFloat(),
@@ -182,6 +188,12 @@
collapsedSize = positioner.collapsedSize
collapsedCornerRadius = collapsedSize / 2
collapsedColor = positioner.collapsedColor
+ collapsedElevation = positioner.collapsedElevation
+
+ // calculate the expansion progress required before we start showing the triangle as part of
+ // the expansion animation
+ minExpansionProgressForTriangle =
+ positioner.distanceToRevealTriangle / tyToCollapsedPosition
// post the request to start the expand animation to the looper so the view can measure
// itself
@@ -240,6 +252,9 @@
message.alpha = alpha
avatar.alpha = alpha
+ translationZ =
+ collapsedElevation + (flyoutElevation - collapsedElevation) * expansionProgress
+
invalidate()
}
@@ -275,7 +290,7 @@
currentCornerRadius,
backgroundPaint,
)
- if (expansionProgress >= MIN_EXPANSION_PROGRESS_FOR_TRIANGLE) {
+ if (expansionProgress >= minExpansionProgressForTriangle) {
drawTriangle(canvas)
}
canvas.restore()
@@ -291,9 +306,20 @@
} else {
width - currentCornerRadius - triangleWidth
}
- // instead of scaling the triangle, increasingly reveal it from the background, starting
- // with half the size. this has the effect of the triangle scaling.
- val triangleY = height - triangleHeight - 0.5f * triangleHeight * (1 - expansionProgress)
+ // instead of scaling the triangle, increasingly reveal it from the background. this has the
+ // effect of the triangle scaling.
+
+ // the translation y of the triangle before we start revealing it. align its bottom with the
+ // bottom of the rect
+ val triangleYCollapsed = height - triangleHeight - (triangleHeight - triangleOverlap)
+ // the translation y of the triangle when it's fully revealed
+ val triangleYExpanded = height - triangleHeight
+ val interpolatedExpansion =
+ ((expansionProgress - minExpansionProgressForTriangle) /
+ (1 - minExpansionProgressForTriangle))
+ .coerceIn(0f, 1f)
+ val triangleY =
+ triangleYCollapsed + (triangleYExpanded - triangleYCollapsed) * interpolatedExpansion
canvas.translate(triangleX, triangleY)
canvas.drawPath(triangle, backgroundPaint)
triangleOutline.setPath(triangle)
@@ -309,7 +335,7 @@
currentCornerRadius,
Path.Direction.CW,
)
- if (expansionProgress >= MIN_EXPANSION_PROGRESS_FOR_TRIANGLE) {
+ if (expansionProgress >= minExpansionProgressForTriangle) {
path.addPath(triangleOutline.mPath)
}
outline.setPath(path)
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
index 9721792..a78890b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
@@ -56,14 +56,29 @@
fun runAfterInit(action: Runnable)
}
+ /** Launcher states bubbles cares about */
+ enum class BubbleLauncherState {
+ /* When launcher is in overview */
+ OVERVIEW,
+ /* When launcher is on home */
+ HOME,
+ /* We're in an app */
+ IN_APP,
+ }
+
+ /** The current launcher state */
+ var launcherState: BubbleLauncherState
+
/** Whether bubble bar is currently stashed */
val isStashed: Boolean
/** Whether launcher enters or exits the home page. */
- var isBubblesShowingOnHome: Boolean
+ val isBubblesShowingOnHome: Boolean
+ get() = launcherState == BubbleLauncherState.HOME
/** Whether launcher enters or exits the overview page. */
- var isBubblesShowingOnOverview: Boolean
+ val isBubblesShowingOnOverview: Boolean
+ get() = launcherState == BubbleLauncherState.OVERVIEW
/** Updated when sysui locked state changes, when locked, bubble bar is not shown. */
var isSysuiLocked: Boolean
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt
index 7d6f7ad..722dfe7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashController.kt
@@ -26,6 +26,7 @@
import com.android.launcher3.taskbar.TaskbarInsetsController
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.BubbleLauncherState
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_STASH_DURATION
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_TRANSLATION_DURATION
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.ControllersAfterInitAction
@@ -45,29 +46,21 @@
private lateinit var bubbleBarScaleAnimator: AnimatedFloat
private lateinit var controllersAfterInitAction: ControllersAfterInitAction
- override var isBubblesShowingOnHome: Boolean = false
- set(onHome) {
- if (field == onHome) return
- field = onHome
+ override var launcherState: BubbleLauncherState = BubbleLauncherState.IN_APP
+ set(state) {
+ if (field == state) return
+ val transitionFromHome = field == BubbleLauncherState.HOME
+ field = state
if (!bubbleBarViewController.hasBubbles()) {
// if there are no bubbles, there's nothing to show, so just return.
return
}
- if (onHome) {
- // When transition to home we should show collapse the bubble bar
- updateExpandedState(expand = false)
- }
- animateBubbleBarY()
- bubbleBarViewController.onBubbleBarConfigurationChanged(/* animate= */ true)
- }
-
- override var isBubblesShowingOnOverview: Boolean = false
- set(onOverview) {
- if (field == onOverview) return
- field = onOverview
- if (!onOverview) {
- // When transition from overview we should show collapse the bubble bar
- updateExpandedState(expand = false)
+ // If we're transitioning anywhere, bubble bar should be collapsed
+ updateExpandedState(expand = false)
+ if (transitionFromHome || field == BubbleLauncherState.HOME) {
+ // If we're transitioning to or from home, animate the Y because we're in hotseat
+ // on home but in persistent taskbar elsewhere so the position is different.
+ animateBubbleBarY()
}
bubbleBarViewController.onBubbleBarConfigurationChanged(/* animate= */ true)
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
index 74e3c00..fe3db30 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
@@ -36,6 +36,7 @@
import com.android.launcher3.taskbar.TaskbarStashController.TRANSIENT_TASKBAR_STASH_ALPHA_DURATION
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.BubbleLauncherState
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_STASH_DURATION
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_TRANSLATION_DURATION
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.ControllersAfterInitAction
@@ -81,36 +82,26 @@
override var isStashed: Boolean = false
@VisibleForTesting set
- override var isBubblesShowingOnHome: Boolean = false
- set(onHome) {
- if (field == onHome) return
- field = onHome
+ override var launcherState: BubbleLauncherState = BubbleLauncherState.IN_APP
+ set(state) {
+ if (field == state) return
+ field = state
if (!bubbleBarViewController.hasBubbles()) {
// if there are no bubbles, there's nothing to show, so just return.
return
}
- if (onHome) {
- updateStashedAndExpandedState(stash = false, expand = false)
- // When transitioning from app to home we need to animate the bubble bar
+ if (field == BubbleLauncherState.HOME) {
+ // When to home we need to animate the bubble bar
// here to align with hotseat center.
animateBubbleBarYToHotseat()
- } else if (!bubbleBarViewController.isExpanded) {
- updateStashedAndExpandedState(stash = true, expand = false)
- }
- bubbleBarViewController.onBubbleBarConfigurationChanged(/* animate= */ true)
- }
-
- override var isBubblesShowingOnOverview: Boolean = false
- set(onOverview) {
- if (field == onOverview) return
- field = onOverview
- if (onOverview) {
+ } else if (field == BubbleLauncherState.OVERVIEW) {
// When transitioning to overview we need to animate the bubble bar to align with
// the taskbar bottom.
animateBubbleBarYToTaskbar()
- } else {
- updateStashedAndExpandedState(stash = true, expand = false)
}
+ // Only stash if we're in an app, otherwise we're in home or overview where we should
+ // be un-stashed
+ updateStashedAndExpandedState(field == BubbleLauncherState.IN_APP, expand = false)
bubbleBarViewController.onBubbleBarConfigurationChanged(/* animate= */ true)
}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 9ad6b303..dc7ed24 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -1149,12 +1149,18 @@
if (endTarget != NEW_TASK) {
InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_QUICK_SWITCH);
+ } else {
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_QUICK_SWITCH);
}
if (endTarget != HOME) {
InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME);
+ } else {
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME);
}
if (endTarget != RECENTS) {
InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS);
+ } else {
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS);
}
switch (endTarget) {
diff --git a/quickstep/src/com/android/quickstep/FocusState.kt b/quickstep/src/com/android/quickstep/FocusState.kt
new file mode 100644
index 0000000..ba3991f
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/FocusState.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep
+
+import android.os.RemoteException
+import android.util.Log
+import android.view.Display.DEFAULT_DISPLAY
+import com.android.launcher3.util.Executors
+import com.android.wm.shell.shared.IFocusTransitionListener.Stub
+import com.android.wm.shell.shared.IShellTransitions
+
+/** Class to track focus state of displays and windows */
+class FocusState {
+
+ var focusedDisplayId = DEFAULT_DISPLAY
+ private set
+
+ private var listeners = mutableSetOf<FocusChangeListener>()
+
+ fun addListener(l: FocusChangeListener) = listeners.add(l)
+
+ fun removeListener(l: FocusChangeListener) = listeners.remove(l)
+
+ fun init(transitions: IShellTransitions?) {
+ try {
+ transitions?.setFocusTransitionListener(
+ object : Stub() {
+ override fun onFocusedDisplayChanged(displayId: Int) {
+ Executors.MAIN_EXECUTOR.execute {
+ listeners.forEach { it.onFocusedDisplayChanged(displayId) }
+ }
+ }
+ }
+ )
+ } catch (e: RemoteException) {
+ Log.w(TAG, "Failed call setFocusTransitionListener", e)
+ }
+ }
+
+ interface FocusChangeListener {
+ fun onFocusedDisplayChanged(displayId: Int)
+ }
+
+ override fun toString() = "{FocusState focusedDisplayId=$focusedDisplayId}"
+
+ companion object {
+ private const val TAG = "FocusState"
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 34435d5..a55cf18 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -161,6 +161,7 @@
private IRemoteAnimationRunner mBackToLauncherRunner;
private IDragAndDrop mDragAndDrop;
private final HomeVisibilityState mHomeVisibilityState = new HomeVisibilityState();
+ private final FocusState mFocusState = new FocusState();
// Used to dedupe calls to SystemUI
private int mLastShelfHeight;
@@ -300,6 +301,7 @@
registerSplitScreenListener(mSplitScreenListener);
registerSplitSelectListener(mSplitSelectListener);
mHomeVisibilityState.init(mShellTransitions);
+ mFocusState.init(mShellTransitions);
setStartingWindowListener(mStartingWindowListener);
setLauncherUnlockAnimationController(
mLauncherActivityClass, mLauncherUnlockAnimationController);
@@ -1144,6 +1146,10 @@
return mHomeVisibilityState;
}
+ public FocusState getFocusState() {
+ return mFocusState;
+ }
+
/**
* Returns a surface which can be used to attach overlays to home task or null if
* the task doesn't exist or sysui is not connected
@@ -1592,6 +1598,7 @@
pw.println("\tmOneHanded=" + mOneHanded);
pw.println("\tmShellTransitions=" + mShellTransitions);
pw.println("\tmHomeVisibilityState=" + mHomeVisibilityState);
+ pw.println("\tmFocusState=" + mFocusState);
pw.println("\tmStartingWindow=" + mStartingWindow);
pw.println("\tmStartingWindowListener=" + mStartingWindowListener);
pw.println("\tmSysuiUnlockAnimationController=" + mSysuiUnlockAnimationController);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
index 5ad55ae..49bff8d 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
@@ -228,10 +228,13 @@
}
float velocityYPxPerS = mVelocityTracker.getYVelocity();
+ float dY = Math.abs(mLastPos.y - mDownPos.y);
if (mCanPlayTaskbarBgAlphaAnimation
&& mMotionMoveCount >= NUM_MOTION_MOVE_THRESHOLD // Arbitrary value
&& velocityYPxPerS != 0 // Ignore these
- && velocityYPxPerS >= mTaskbarSlowVelocityYThreshold) {
+ && velocityYPxPerS >= mTaskbarSlowVelocityYThreshold
+ && dY != 0
+ && dY > mTouchSlop) {
mTaskbarActivityContext.playTaskbarBackgroundAlphaAnimation();
mCanPlayTaskbarBgAlphaAnimation = false;
}
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 5028da4..9510a05 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -52,7 +52,6 @@
import androidx.annotation.StringRes;
import androidx.annotation.StyleRes;
import androidx.appcompat.app.AlertDialog;
-import androidx.appcompat.content.res.AppCompatResources;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
@@ -610,8 +609,8 @@
private void updateDrawables() {
if (mContext != null) {
- mTutorialFragment.getRootView().setBackground(AppCompatResources.getDrawable(
- mContext, getMockWallpaperResId()));
+ mTutorialFragment.getRootView()
+ .setBackground(mContext.getDrawable(getMockWallpaperResId()));
mTutorialFragment.updateFeedbackAnimation();
mFakeLauncherView.setBackgroundColor(getFakeLauncherColor());
updateFakeViewLayout(mFakeHotseatView, getMockHotseatResId());
@@ -619,9 +618,7 @@
mFakeTaskView.animate().alpha(1).setListener(
AnimatorListeners.forSuccessCallback(() -> mFakeTaskView.animate().cancel()));
mFakePreviousTaskView.setFakeTaskViewFillColor(getMockPreviousAppTaskThumbnailColor());
- mFakeIconView.setBackground(AppCompatResources.getDrawable(
- mContext, getMockAppIconResId()));
-
+ mFakeIconView.setBackground(mContext.getDrawable(getMockAppIconResId()));
mExitingAppView.setBackgroundColor(getExitingAppColor());
mFakeTaskView.setBackgroundColor(getFakeTaskViewColor());
updateHotseatChildViewColor(mHotseatIconView);
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java b/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java
index ae0e725..f1fc179 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java
@@ -22,8 +22,6 @@
import android.widget.ImageView;
import android.widget.LinearLayout;
-import androidx.appcompat.content.res.AppCompatResources;
-
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.GraphicsUtils;
@@ -91,9 +89,8 @@
int inactiveStepIndicatorColor = GraphicsUtils.getAttrColor(
getContext(), android.R.attr.textColorSecondaryInverse);
for (int i = 0; i < mTotalSteps; i++) {
- Drawable pageIndicatorPillDrawable = AppCompatResources.getDrawable(
- getContext(), R.drawable.tutorial_step_indicator_pill);
-
+ Drawable pageIndicatorPillDrawable =
+ getContext().getDrawable(R.drawable.tutorial_step_indicator_pill);
if (i >= getChildCount()) {
ImageView pageIndicatorPill = new ImageView(getContext());
pageIndicatorPill.setImageDrawable(pageIndicatorPillDrawable);
diff --git a/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt
index 68690d2..c3b072d 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt
+++ b/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt
@@ -113,6 +113,12 @@
it.isVisibleInCarousel(runningTaskView, nonRunningTaskCarouselHidden)
}
+ /** Returns if any small tasks are fully visible */
+ fun isAnySmallTaskFullyVisible(
+ taskViews: Iterable<TaskView>,
+ isTaskViewFullyVisible: (TaskView) -> Boolean,
+ ): Boolean = taskViews.any { !it.isLargeTile && isTaskViewFullyVisible(it) }
+
/** Returns the current list of [TaskView] children. */
fun getTaskViews(taskViewCount: Int, requireTaskViewAt: (Int) -> TaskView): Iterable<TaskView> =
(0 until taskViewCount).map(requireTaskViewAt)
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index 3449cf2..f708f4b 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -238,7 +238,7 @@
taskViewHeight,
)
val snapshotViewSize =
- if (isPrimaryTaskSplitting) primarySnapshotViewSize else secondarySnapshotViewSize
+ if (isPrimaryTaskSplitting) secondarySnapshotViewSize else primarySnapshotViewSize
if (deviceProfile.isLeftRightSplit) {
// Center view first so scaling happens uniformly, alternatively we can move pivotX to 0
val centerThumbnailTranslationX: Float = (taskViewWidth - snapshotViewSize.x) / 2f
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index c7777d8..f5be103 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -305,6 +305,14 @@
}
/**
+ * Override the pivot used to apply scale changes.
+ */
+ public void setPivotOverride(PointF pivotOverride) {
+ mPivotOverride = pivotOverride;
+ getFullScreenScale();
+ }
+
+ /**
* Adds animation for all the components corresponding to transition from an app to overview.
*/
public void addAppToOverviewAnim(PendingAnimation pa, Interpolator interpolator) {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index a469743..8b3ef04 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1706,10 +1706,11 @@
return;
}
TaskView taskView = getTaskViewAt(mNextPage);
- // Snap to fully visible focused task and clear all button.
boolean shouldSnapToLargeTask = taskView != null && taskView.isLargeTile()
- && isTaskViewFullyVisible(taskView);
+ && !mUtils.isAnySmallTaskFullyVisible(getTaskViews(),
+ this::isTaskViewFullyVisible);
boolean shouldSnapToClearAll = mNextPage == indexOfChild(mClearAllButton);
+ // Snap to large tile when grid tasks aren't fully visible or the clear all button.
if (!shouldSnapToLargeTask && !shouldSnapToClearAll) {
return;
}
@@ -3611,10 +3612,10 @@
float dismissedTaskWidth = 0;
float nextFocusedTaskWidth = 0;
- // Non-grid specific properties.
int[] oldScroll = new int[count];
int[] newScroll = new int[count];
int scrollDiffPerPage = 0;
+ // Non-grid specific properties.
boolean needsCurveUpdates = false;
if (showAsGrid) {
@@ -3644,13 +3645,13 @@
}
}
}
- } else {
- getPageScrolls(oldScroll, false, SIMPLE_SCROLL_LOGIC);
- getPageScrolls(newScroll, false,
- v -> v.getVisibility() != GONE && v != dismissedTaskView);
- if (count > 1) {
- scrollDiffPerPage = Math.abs(oldScroll[1] - oldScroll[0]);
- }
+ }
+
+ getPageScrolls(oldScroll, false, SIMPLE_SCROLL_LOGIC);
+ getPageScrolls(newScroll, false,
+ v -> v.getVisibility() != GONE && v != dismissedTaskView);
+ if (count > 1) {
+ scrollDiffPerPage = Math.abs(oldScroll[1] - oldScroll[0]);
}
float dismissTranslationInterpolationEnd = 1;
@@ -3802,7 +3803,9 @@
addDismissedTaskAnimations(dismissedTaskView, duration, anim);
}
}
- } else if (!showAsGrid) {
+ } else if (!showAsGrid || (enableLargeDesktopWindowingTile()
+ && dismissedTaskView.isLargeTile()
+ && nextFocusedTaskView == null)) {
int offset = getOffsetToDismissedTask(scrollDiffPerPage, dismissedIndex, taskCount);
int scrollDiff = newScroll[i] - oldScroll[i] + offset;
if (scrollDiff != 0) {
@@ -3814,18 +3817,16 @@
splitTimings);
needsCurveUpdates = true;
}
- } else if (child instanceof TaskView) {
- TaskView taskView = (TaskView) child;
+ } else if (child instanceof TaskView taskView) {
if (isFocusedTaskDismissed) {
if (nextFocusedTaskView != null &&
!isSameGridRow(taskView, nextFocusedTaskView)) {
continue;
}
- } else {
- if (i < dismissedIndex || !isSameGridRow(taskView, dismissedTaskView)) {
- continue;
- }
+ } else if (i < dismissedIndex || !isSameGridRow(taskView, dismissedTaskView)) {
+ continue;
}
+
// Animate task with index >= dismissed index and in the same row as the
// dismissed index or next focused index. Offset successive task dismissal
// durations for a staggered effect.
@@ -4136,15 +4137,18 @@
* - Dragging an adjacent page on the left side (right side for RTL)
*/
private int getOffsetToDismissedTask(int scrollDiffPerPage, int dismissedIndex, int taskCount) {
+ // When mCurrentPage is ClearAllButton, use the last TaskView instead to calculate
+ // offset.
+ int currentPage = mCurrentPage == taskCount ? taskCount - 1 : mCurrentPage;
int offset = mIsRtl ? scrollDiffPerPage : 0;
- if (mCurrentPage == dismissedIndex) {
+ if (currentPage == dismissedIndex) {
int lastPage = taskCount - 1;
- if (mCurrentPage == lastPage) {
+ if (currentPage == lastPage) {
offset += mIsRtl ? -scrollDiffPerPage : scrollDiffPerPage;
}
} else {
// Dismissing an adjacent page.
- int negativeAdjacent = mCurrentPage - 1; // (Right in RTL, left in LTR)
+ int negativeAdjacent = currentPage - 1; // (Right in RTL, left in LTR)
if (dismissedIndex == negativeAdjacent) {
offset += mIsRtl ? -scrollDiffPerPage : scrollDiffPerPage;
}
@@ -5432,6 +5436,13 @@
mTempPointF);
setPivotX(mTempPointF.x);
setPivotY(mTempPointF.y);
+ runActionOnRemoteHandles(
+ remoteTargetHandle -> {
+ remoteTargetHandle.getTaskViewSimulator().setPivotOverride(
+ mTempPointF);
+ remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(
+ false);
+ });
}
});
} else if (!showAsGrid) {
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutViewScreenshotTest.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutViewScreenshotTest.kt
index 50e2dcf..6aba6a3 100644
--- a/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutViewScreenshotTest.kt
+++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutViewScreenshotTest.kt
@@ -224,7 +224,7 @@
BubbleBarFlyoutMessage(
senderAvatar = ColorDrawable(Color.RED),
senderName = "sender",
- message = "collapsed 90% on left",
+ message = "expanded 90% on left",
isGroupChat = true,
)
) {}
@@ -249,7 +249,7 @@
BubbleBarFlyoutMessage(
senderAvatar = ColorDrawable(Color.RED),
senderName = "sender",
- message = "collapsed 80% on right",
+ message = "expanded 80% on right",
isGroupChat = true,
)
) {}
@@ -265,5 +265,7 @@
override val targetTy = 0f
override val collapsedSize = 30f
override val collapsedColor = Color.BLUE
+ override val collapsedElevation = 1f
+ override val distanceToRevealTriangle = 10f
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt
index 0f02444..d857ae5 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt
@@ -55,6 +55,8 @@
override val distanceToCollapsedPosition = PointF(100f, 200f)
override val collapsedSize = 30f
override val collapsedColor = Color.BLUE
+ override val collapsedElevation = 1f
+ override val distanceToRevealTriangle = 50f
}
flyoutController = BubbleBarFlyoutController(flyoutContainer, positioner)
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt
index 4106a2c..5dc78a9 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/PersistentBubbleStashControllerTest.kt
@@ -27,6 +27,7 @@
import com.android.launcher3.taskbar.TaskbarInsetsController
import com.android.launcher3.taskbar.bubbles.BubbleBarView
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.BubbleLauncherState
import com.android.launcher3.util.MultiValueAlpha
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -85,12 +86,12 @@
fun setBubblesShowingOnHomeUpdatedToFalse_barPositionYUpdated_controllersNotified() {
// Given bubble bar is on home and has bubbles
whenever(bubbleBarViewController.hasBubbles()).thenReturn(false)
- persistentTaskBarStashController.isBubblesShowingOnHome = true
+ persistentTaskBarStashController.launcherState = BubbleLauncherState.HOME
whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
// When switch out of the home screen
getInstrumentation().runOnMainSync {
- persistentTaskBarStashController.isBubblesShowingOnHome = false
+ persistentTaskBarStashController.launcherState = BubbleLauncherState.IN_APP
}
// Then translation Y is animating and the bubble bar controller is notified
@@ -110,7 +111,7 @@
// When switch to home screen
getInstrumentation().runOnMainSync {
- persistentTaskBarStashController.isBubblesShowingOnHome = true
+ persistentTaskBarStashController.launcherState = BubbleLauncherState.HOME
}
// Then translation Y is animating and the bubble bar controller is notified
@@ -127,11 +128,11 @@
@Test
fun setBubblesShowingOnOverviewUpdatedToFalse_controllersNotified() {
// Given bubble bar is on overview
- persistentTaskBarStashController.isBubblesShowingOnOverview = true
+ persistentTaskBarStashController.launcherState = BubbleLauncherState.OVERVIEW
clearInvocations(bubbleBarViewController)
// When switch out of the overview screen
- persistentTaskBarStashController.isBubblesShowingOnOverview = false
+ persistentTaskBarStashController.launcherState = BubbleLauncherState.IN_APP
// Then bubble bar controller is notified
verify(bubbleBarViewController).onBubbleBarConfigurationChanged(/* animate= */ true)
@@ -140,7 +141,7 @@
@Test
fun setBubblesShowingOnOverviewUpdatedToTrue_controllersNotified() {
// When switch to the overview screen
- persistentTaskBarStashController.isBubblesShowingOnOverview = true
+ persistentTaskBarStashController.launcherState = BubbleLauncherState.OVERVIEW
// Then bubble bar controller is notified
verify(bubbleBarViewController).onBubbleBarConfigurationChanged(/* animate= */ true)
@@ -150,7 +151,7 @@
fun isSysuiLockedSwitchedToFalseForOverview_unlockAnimationIsShown() {
// Given screen is locked and bubble bar has bubbles
persistentTaskBarStashController.isSysuiLocked = true
- persistentTaskBarStashController.isBubblesShowingOnOverview = true
+ persistentTaskBarStashController.launcherState = BubbleLauncherState.OVERVIEW
whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
// When switch to the overview screen
@@ -211,14 +212,14 @@
fun bubbleBarTranslationYForTaskbar() {
// Give bubble bar is on home
whenever(bubbleBarViewController.hasBubbles()).thenReturn(false)
- persistentTaskBarStashController.isBubblesShowingOnHome = true
+ persistentTaskBarStashController.launcherState = BubbleLauncherState.HOME
// Then bubbleBarTranslationY would be HOTSEAT_TRANSLATION_Y
assertThat(persistentTaskBarStashController.bubbleBarTranslationY)
.isEqualTo(HOTSEAT_TRANSLATION_Y)
// Give bubble bar is not on home
- persistentTaskBarStashController.isBubblesShowingOnHome = false
+ persistentTaskBarStashController.launcherState = BubbleLauncherState.IN_APP
// Then bubbleBarTranslationY would be TASK_BAR_TRANSLATION_Y
assertThat(persistentTaskBarStashController.bubbleBarTranslationY)
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt
index d4a3b3a..7973e2df 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt
@@ -29,13 +29,16 @@
import com.android.launcher3.anim.AnimatedFloat
import com.android.launcher3.taskbar.StashedHandleView
import com.android.launcher3.taskbar.TaskbarInsetsController
+import com.android.launcher3.taskbar.TaskbarStashController
import com.android.launcher3.taskbar.bubbles.BubbleBarView
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController
import com.android.launcher3.taskbar.bubbles.BubbleView
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.BubbleLauncherState
import com.android.launcher3.util.MultiValueAlpha
import com.android.wm.shell.shared.animation.PhysicsAnimator
import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
+import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
@@ -59,7 +62,7 @@
const val BUBBLE_BAR_WIDTH = 200
const val BUBBLE_BAR_HEIGHT = 100
const val HOTSEAT_TRANSLATION_Y = -45f
- const val TASK_BAR_TRANSLATION_Y = -TASKBAR_BOTTOM_SPACE
+ const val TASK_BAR_TRANSLATION_Y = -TASKBAR_BOTTOM_SPACE.toFloat()
const val HANDLE_VIEW_WIDTH = 150
const val HANDLE_VIEW_HEIGHT = 4
const val BUBBLE_BAR_STASHED_TRANSLATION_Y = -4.5f
@@ -119,7 +122,7 @@
// When switch out of the home screen
getInstrumentation().runOnMainSync {
- mTransientBubbleStashController.isBubblesShowingOnHome = true
+ mTransientBubbleStashController.launcherState = BubbleLauncherState.HOME
}
// Then BubbleBarView is animating, BubbleBarViewController controller is notified
@@ -137,12 +140,12 @@
@Test
fun setBubblesShowingOnOverviewUpdatedToTrue_barPositionYUpdated_controllersNotified() {
- // Given bubble bar is on home and has bubbles
+ // Given bubble bar is on overview and has bubbles
whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
// When switch out of the home screen
getInstrumentation().runOnMainSync {
- mTransientBubbleStashController.isBubblesShowingOnOverview = true
+ mTransientBubbleStashController.launcherState = BubbleLauncherState.OVERVIEW
}
// Then BubbleBarView is animating, BubbleBarViewController controller is notified
@@ -159,6 +162,27 @@
}
@Test
+ fun setBubblesShowingOnOverviewUpdatedToTrue_unstashes() {
+ // Given bubble bar is stashed with bubbles
+ whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
+
+ getInstrumentation().runOnMainSync {
+ mTransientBubbleStashController.updateStashedAndExpandedState(
+ stash = true,
+ expand = false,
+ )
+ }
+ assertThat(mTransientBubbleStashController.isStashed).isTrue()
+
+ // Move to overview
+ getInstrumentation().runOnMainSync {
+ mTransientBubbleStashController.launcherState = BubbleLauncherState.OVERVIEW
+ }
+ // No longer stashed in overview
+ assertThat(mTransientBubbleStashController.isStashed).isFalse()
+ }
+
+ @Test
fun updateStashedAndExpandedState_stashAndCollapse_bubbleBarHidden_stashedHandleShown() {
// Given bubble bar has bubbles and not stashed
mTransientBubbleStashController.isStashed = false
@@ -196,11 +220,98 @@
}
@Test
+ fun updateStashedAndExpandedState_unstash_bubbleBarShown_stashedHandleHidden() {
+ // Given bubble bar has bubbles and is stashed
+ mTransientBubbleStashController.isStashed = true
+ whenever(bubbleBarViewController.isHiddenForNoBubbles).thenReturn(false)
+
+ val bubbleInitialTranslation = bubbleView.translationY
+
+ // When unstash
+ getInstrumentation().runOnMainSync {
+ mTransientBubbleStashController.updateStashedAndExpandedState(
+ stash = false,
+ expand = false,
+ )
+ }
+
+ // Wait until animations ends
+ advanceTimeBy(BubbleStashController.BAR_STASH_DURATION)
+ PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(DynamicAnimation.TRANSLATION_Y)
+
+ // Then check BubbleBarController is notified
+ verify(bubbleBarViewController).onStashStateChanging()
+ // Bubble bar is unstashed
+ assertThat(mTransientBubbleStashController.isStashed).isFalse()
+ assertThat(bubbleBarView.translationY).isEqualTo(TASK_BAR_TRANSLATION_Y)
+ assertThat(bubbleBarView.alpha).isEqualTo(1f)
+ assertThat(bubbleBarView.scaleX).isEqualTo(1f)
+ assertThat(bubbleBarView.scaleY).isEqualTo(1f)
+ assertThat(bubbleBarView.background.alpha).isEqualTo(255)
+ // Handle view is hidden
+ assertThat(stashedHandleView.translationY).isEqualTo(0)
+ assertThat(stashedHandleView.alpha).isEqualTo(0)
+ // Bubble view is reset
+ assertThat(bubbleView.translationY).isEqualTo(bubbleInitialTranslation)
+ assertThat(bubbleView.alpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun updateStashedAndExpandedState_stash_animatesAlphaForBubblesAndBackgroundSeparately() {
+ // Given bubble bar has bubbles and is unstashed
+ mTransientBubbleStashController.isStashed = false
+ whenever(bubbleBarViewController.isHiddenForNoBubbles).thenReturn(false)
+
+ // When stash
+ getInstrumentation().runOnMainSync {
+ mTransientBubbleStashController.updateStashedAndExpandedState(
+ stash = true,
+ expand = false,
+ )
+ }
+
+ // Stop after alpha starts
+ advanceTimeBy(TaskbarStashController.TASKBAR_STASH_ALPHA_START_DELAY + 10)
+
+ // Bubble bar alpha is set to 1
+ assertThat(bubbleBarView.alpha).isEqualTo(1f)
+ // We animate alpha for background and children separately
+ assertThat(bubbleView.alpha).isIn(Range.open(0f, 1f))
+ assertThat(bubbleBarView.background.alpha).isIn(Range.open(0, 255))
+ assertThat(bubbleBarView.background.alpha).isNotEqualTo((bubbleView.alpha * 255f).toInt())
+ }
+
+ @Test
+ fun updateStashedAndExpandedState_unstash_animatesAlphaForBubblesAndBackgroundSeparately() {
+ // Given bubble bar has bubbles and is stashed
+ mTransientBubbleStashController.isStashed = true
+ whenever(bubbleBarViewController.isHiddenForNoBubbles).thenReturn(false)
+
+ // When unstash
+ getInstrumentation().runOnMainSync {
+ mTransientBubbleStashController.updateStashedAndExpandedState(
+ stash = false,
+ expand = false,
+ )
+ }
+
+ // Stop after alpha starts
+ advanceTimeBy(TaskbarStashController.TASKBAR_STASH_ALPHA_START_DELAY + 10)
+
+ // Bubble bar alpha is set to 1
+ assertThat(bubbleBarView.alpha).isEqualTo(1f)
+ // We animate alpha for background and children separately
+ assertThat(bubbleView.alpha).isIn(Range.open(0f, 1f))
+ assertThat(bubbleBarView.background.alpha).isIn(Range.open(0, 255))
+ assertThat(bubbleBarView.background.alpha).isNotEqualTo((bubbleView.alpha * 255f).toInt())
+ }
+
+ @Test
fun isSysuiLockedSwitchedToFalseForOverview_unlockAnimationIsShown() {
// Given screen is locked and bubble bar has bubbles
getInstrumentation().runOnMainSync {
mTransientBubbleStashController.isSysuiLocked = true
- mTransientBubbleStashController.isBubblesShowingOnOverview = true
+ mTransientBubbleStashController.launcherState = BubbleLauncherState.OVERVIEW
whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
}
advanceTimeBy(BubbleStashController.BAR_TRANSLATION_DURATION)
diff --git a/res/layout/widgets_two_pane_sheet_paged_view.xml b/res/layout/widgets_two_pane_sheet_paged_view.xml
index 1cbd2ba..71c77b5 100644
--- a/res/layout/widgets_two_pane_sheet_paged_view.xml
+++ b/res/layout/widgets_two_pane_sheet_paged_view.xml
@@ -20,16 +20,19 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="start"
- android:paddingHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
android:layout_gravity="start"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_alignParentStart="true">
+ <!-- Note: the paddingHorizontal has to be on WidgetPagedView level so that talkback
+ correctly orders the lists to be after the search and suggestions header. See b/209579563.
+ -->
<com.android.launcher3.widget.picker.WidgetPagedView
android:id="@+id/widgets_view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
android:descendantFocusability="afterDescendants"
launcher:pageIndicator="@+id/tabs" >
@@ -48,11 +51,13 @@
</com.android.launcher3.widget.picker.WidgetPagedView>
<!-- SearchAndRecommendationsView without the tab layout as well -->
+ <!-- Note: the horizontal padding matches with the WidgetPagedView -->
<com.android.launcher3.views.StickyHeaderLayout
android:id="@+id/search_and_recommendations_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToOutline="true"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
android:orientation="vertical">
<LinearLayout
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index aee49be..f7b04a3 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -31,8 +31,7 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"App info for %1$s"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"Usage settings for %1$s"</string>
- <!-- no translation found for new_window_option_taskbar (6448780542727767211) -->
- <skip />
+ <string name="new_window_option_taskbar" msgid="6448780542727767211">"New window"</string>
<string name="save_app_pair" msgid="5647523853662686243">"Save app pair"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"This app pair isn\'t supported on this device"</string>
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 572274e..9192e13 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -420,6 +420,25 @@
return mapRange(interpolator.getInterpolation(progress), toMin, toMax);
}
+ /**
+ * Maps t from one range to another range.
+ * @param t The value to map.
+ * @param fromMin The lower bound of the range that t is being mapped from.
+ * @param fromMax The upper bound of the range that t is being mapped from.
+ * @param toMin The lower bound of the range that t is being mapped to.
+ * @param toMax The upper bound of the range that t is being mapped to.
+ * @return The mapped value of t.
+ */
+ public static int mapToRange(int t, int fromMin, int fromMax, int toMin, int toMax,
+ Interpolator interpolator) {
+ if (fromMin == fromMax || toMin == toMax) {
+ Log.e(TAG, "mapToRange: range has 0 length");
+ return toMin;
+ }
+ float progress = getProgress(t, fromMin, fromMax);
+ return (int) mapRange(interpolator.getInterpolation(progress), toMin, toMax);
+ }
+
/** Bounds t between a lower and upper bound and maps the result to a range. */
public static float mapBoundToRange(float t, float lowerBound, float upperBound,
float toMin, float toMax, Interpolator interpolator) {
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index e215cab..6a40121 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -66,6 +66,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedPropertySetter;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.icons.BitmapInfo;
@@ -699,7 +700,9 @@
mAllApps.mAH.get(MAIN).mRecyclerView.removeItemDecoration(
mPrivateAppsSectionDecorator);
// Call onAppsUpdated() because it may be canceled when this animation occurs.
- mAllApps.getPersonalAppList().onAppsUpdated();
+ if (!Utilities.isRunningInTestHarness()) {
+ mAllApps.getPersonalAppList().onAppsUpdated();
+ }
if (isPrivateSpaceHidden()) {
// TODO (b/325455879): Figure out if we can avoid this.
getMainRecyclerView().getAdapter().notifyDataSetChanged();
diff --git a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
index 0a50e8b..9f6b40b 100644
--- a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
+++ b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
@@ -18,6 +18,7 @@
import android.content.Context;
+import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.util.DaggerSingletonTracker;
import dagger.BindsInstance;
@@ -32,6 +33,8 @@
*/
public interface LauncherBaseAppComponent {
DaggerSingletonTracker getDaggerSingletonTracker();
+ InstallSessionHelper getInstallSessionHelper();
+
/** Builder for LauncherBaseAppComponent. */
interface Builder {
@BindsInstance Builder appContext(@ApplicationContext Context context);
diff --git a/src/com/android/launcher3/icons/CacheableShortcutInfo.kt b/src/com/android/launcher3/icons/CacheableShortcutInfo.kt
index c121340..c673bb3 100644
--- a/src/com/android/launcher3/icons/CacheableShortcutInfo.kt
+++ b/src/com/android/launcher3/icons/CacheableShortcutInfo.kt
@@ -110,8 +110,6 @@
info?.let { max(info.shortcutInfo.lastChangedTimestamp, packageInfo.lastUpdateTime) }
?: packageInfo.lastUpdateTime
- override fun addToMemCache() = false
-
override fun getApplicationInfo(info: CacheableShortcutInfo) = info.appInfo.getInfo()
override fun loadIcon(context: Context, cache: BaseIconCache, info: CacheableShortcutInfo) =
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 5396597..ffed1e8 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -110,8 +110,7 @@
IconProvider iconProvider) {
super(context, dbFileName, MODEL_EXECUTOR.getLooper(),
idp.fillResIconDpi, idp.iconBitmapSize, true /* inMemoryCache */, iconProvider);
- mComponentWithLabelCachingLogic = new CachedObjectCachingLogic(
- context, false /* loadIcons */, false /* addToMemCache */);
+ mComponentWithLabelCachingLogic = new CachedObjectCachingLogic(context);
mLauncherActivityInfoCachingLogic = LauncherActivityCachingLogic.INSTANCE;
mLauncherApps = mContext.getSystemService(LauncherApps.class);
mUserManager = UserCache.INSTANCE.get(mContext);
@@ -149,8 +148,7 @@
PackageManager.GET_UNINSTALLED_PACKAGES);
long userSerial = mUserManager.getSerialNumberForUser(user);
for (LauncherActivityInfo app : mLauncherApps.getActivityList(packageName, user)) {
- addIconToDBAndMemCache(app, mLauncherActivityInfoCachingLogic, info, userSerial,
- false /*replace existing*/);
+ addIconToDBAndMemCache(app, mLauncherActivityInfoCachingLogic, info, userSerial);
}
} catch (NameNotFoundException e) {
Log.d(TAG, "Package not found", e);
@@ -205,7 +203,7 @@
CancellableTask<ItemInfoWithIcon> request = new CancellableTask<>(
task, MAIN_EXECUTOR, caller::reapplyItemInfo, endRunnable);
- Utilities.postAsyncCallback(mWorkerHandler, request);
+ Utilities.postAsyncCallback(workerHandler, request);
return request;
}
@@ -257,6 +255,7 @@
/**
* Fill in {@code info} with the icon and label for {@code si}. If the icon is not
* available, and fallback check returns true, it keeps the old icon.
+ * Shortcut entries are not kept in memory since they are not frequently used
*/
public <T extends ItemInfoWithIcon> void getShortcutIcon(T info, CacheableShortcutInfo si,
@NonNull Predicate<T> fallbackIconCheck) {
@@ -266,7 +265,7 @@
user,
() -> si,
CacheableShortcutCachingLogic.INSTANCE,
- LookupFlag.DEFAULT).bitmap;
+ LookupFlag.SKIP_ADD_TO_MEM_CACHE).bitmap;
if (bitmapInfo.isNullOrLowRes()) {
bitmapInfo = getDefaultIcon(user);
}
@@ -337,7 +336,8 @@
*/
public synchronized String getTitleNoCache(ComponentWithLabel info) {
CacheEntry entry = cacheLocked(info.getComponent(), info.getUser(), () -> info,
- mComponentWithLabelCachingLogic, LookupFlag.USE_LOW_RES);
+ mComponentWithLabelCachingLogic,
+ LookupFlag.USE_LOW_RES | LookupFlag.SKIP_ADD_TO_MEM_CACHE);
return Utilities.trim(entry.title);
}
diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java
index 124907f..f36f595 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -31,12 +31,17 @@
import com.android.launcher3.Flags;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.SessionCommitReceiver;
+import com.android.launcher3.dagger.ApplicationContext;
+import com.android.launcher3.dagger.LauncherAppSingleton;
+import com.android.launcher3.dagger.LauncherBaseAppComponent;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.ItemInstallQueue;
import com.android.launcher3.util.ApplicationInfoWrapper;
+import com.android.launcher3.util.DaggerSingletonObject;
+import com.android.launcher3.util.DaggerSingletonTracker;
+import com.android.launcher3.util.ExecutorUtil;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
-import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.SafeCloseable;
@@ -47,10 +52,13 @@
import java.util.List;
import java.util.Objects;
+import javax.inject.Inject;
+
/**
* Utility class to tracking install sessions
*/
@SuppressWarnings("NewApi")
+@LauncherAppSingleton
public class InstallSessionHelper implements SafeCloseable {
@NonNull
@@ -64,8 +72,8 @@
private static final boolean DEBUG = false;
@NonNull
- public static final MainThreadInitializedObject<InstallSessionHelper> INSTANCE =
- new MainThreadInitializedObject<>(InstallSessionHelper::new);
+ public static final DaggerSingletonObject<InstallSessionHelper> INSTANCE =
+ new DaggerSingletonObject<>(LauncherBaseAppComponent::getInstallSessionHelper);
@Nullable
private final LauncherApps mLauncherApps;
@@ -82,10 +90,13 @@
@Nullable
private IntSet mPromiseIconIds;
- public InstallSessionHelper(@NonNull final Context context) {
+ @Inject
+ public InstallSessionHelper(@NonNull @ApplicationContext final Context context,
+ DaggerSingletonTracker tracker) {
mInstaller = context.getPackageManager().getPackageInstaller();
mAppContext = context.getApplicationContext();
mLauncherApps = context.getSystemService(LauncherApps.class);
+ ExecutorUtil.executeSyncOnMainOrFail(() -> tracker.addCloseable(this));
}
@Override
diff --git a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index b5932d5..4e9143e 100644
--- a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -167,7 +167,6 @@
public static final String PERMANENT_DIAG_TAG = "TaplTarget";
public static final String ICON_MISSING = "b/282963545";
- public static final String UIOBJECT_STALE_ELEMENT = "b/319501259";
public static final String WIDGET_CONFIG_NULL_EXTRA_INTENT = "b/324419890";
public static final String REQUEST_FLAG_ENABLE_GRID_ONLY_OVERVIEW = "enable-grid-only-overview";
public static final String REQUEST_FLAG_ENABLE_APP_PAIRS = "enable-app-pairs";
diff --git a/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheTest.java b/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheTest.java
index 495d583..1f0e750 100644
--- a/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheTest.java
@@ -15,21 +15,39 @@
*/
package com.android.launcher3.icons;
+import static android.os.Process.myUserHandle;
+
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.android.launcher3.icons.IconCache.EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE;
+import static com.android.launcher3.icons.IconCacheUpdateHandlerTestKt.waitForUpdateHandlerToFinish;
+import static com.android.launcher3.model.data.AppInfo.makeLaunchIntent;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY;
+import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
+import static com.android.launcher3.util.TestUtil.runOnExecutorSync;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutInfo.Builder;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.drawable.Icon;
import android.os.PersistableBundle;
+import android.os.UserHandle;
import android.text.TextUtils;
import androidx.annotation.Nullable;
@@ -37,15 +55,30 @@
import androidx.test.filters.SmallTest;
import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.icons.cache.CachingLogic;
+import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
+import com.android.launcher3.icons.cache.LauncherActivityCachingLogic;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.settings.SettingsActivity;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.ApplicationInfoWrapper;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.RoboApiWrapper;
+
+import com.google.common.truth.Truth;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class IconCacheTest {
@@ -112,6 +145,102 @@
assertEquals(((PackageItemInfo) item).packageName, otherPackage);
}
+ @Test
+ public void launcherActivityInfo_cached_in_memory() {
+ RoboApiWrapper.INSTANCE.initialize();
+ ComponentName cn = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
+ UserHandle user = myUserHandle();
+ ComponentKey cacheKey = new ComponentKey(cn, user);
+
+ LauncherActivityInfo lai = mContext.getSystemService(LauncherApps.class)
+ .resolveActivity(makeLaunchIntent(cn), user);
+ assertNotNull(lai);
+
+ WorkspaceItemInfo info = new WorkspaceItemInfo();
+ info.intent = makeLaunchIntent(cn);
+ runOnExecutorSync(MODEL_EXECUTOR,
+ () -> mIconCache.getTitleAndIcon(info, lai, false));
+ assertNotNull(info.bitmap);
+ assertFalse(info.bitmap.isLowRes());
+
+ // Verify that icon is in memory cache
+ runOnExecutorSync(MODEL_EXECUTOR,
+ () -> assertNotNull(mIconCache.getInMemoryEntryLocked(cacheKey)));
+
+ // Schedule async update and wait for it to complete
+ Set<PackageUserKey> updates =
+ executeIconUpdate(lai, LauncherActivityCachingLogic.INSTANCE);
+
+ // Verify that the icon was not updated and is still in memory cache
+ Truth.assertThat(updates).isEmpty();
+ runOnExecutorSync(MODEL_EXECUTOR,
+ () -> assertNotNull(mIconCache.getInMemoryEntryLocked(cacheKey)));
+ }
+
+ @Test
+ public void shortcutInfo_not_cached_in_memory() {
+ CacheableShortcutInfo si = mockShortcutInfo(0);
+ ShortcutKey cacheKey = ShortcutKey.fromInfo(si.getShortcutInfo());
+
+ WorkspaceItemInfo info = new WorkspaceItemInfo();
+ runOnExecutorSync(MODEL_EXECUTOR, () -> mIconCache.getShortcutIcon(info, si));
+ assertNotNull(info.bitmap);
+ assertFalse(info.bitmap.isLowRes());
+
+ // Verify that icon is in memory cache
+ runOnExecutorSync(MODEL_EXECUTOR,
+ () -> assertNull(mIconCache.getInMemoryEntryLocked(cacheKey)));
+
+ Set<PackageUserKey> updates =
+ executeIconUpdate(si, CacheableShortcutCachingLogic.INSTANCE);
+ // Verify that the icon was not updated and is still in memory cache
+ Truth.assertThat(updates).isEmpty();
+ runOnExecutorSync(MODEL_EXECUTOR,
+ () -> assertNull(mIconCache.getInMemoryEntryLocked(cacheKey)));
+
+ // Now update the shortcut with a newer version
+ updates = executeIconUpdate(
+ mockShortcutInfo(System.currentTimeMillis() + 2000),
+ CacheableShortcutCachingLogic.INSTANCE);
+
+ // Verify that icon was updated but it is still not in mem-cache
+ Truth.assertThat(updates).containsExactly(
+ new PackageUserKey(cacheKey.getPackageName(), cacheKey.user));
+ runOnExecutorSync(MODEL_EXECUTOR,
+ () -> assertNull(mIconCache.getInMemoryEntryLocked(cacheKey)));
+ }
+
+ /**
+ * Executes the icon update for the provided entry and returns the updated packages
+ */
+ private <T> Set<PackageUserKey> executeIconUpdate(T object, CachingLogic<T> cachingLogic) {
+ HashSet<PackageUserKey> updates = new HashSet<>();
+
+ runOnExecutorSync(MODEL_EXECUTOR, () -> {
+ IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
+ updateHandler.updateIcons(
+ Collections.singletonList(object),
+ cachingLogic,
+ (a, b) -> a.forEach(p -> updates.add(new PackageUserKey(p, b))));
+ updateHandler.finish();
+ });
+ waitForUpdateHandlerToFinish(mIconCache);
+ return updates;
+ }
+
+ private CacheableShortcutInfo mockShortcutInfo(long updateTime) {
+ ShortcutInfo info = new ShortcutInfo.Builder(
+ getInstrumentation().getContext(), "test-shortcut")
+ .setIntent(new Intent(Intent.ACTION_VIEW))
+ .setShortLabel("Test")
+ .setIcon(Icon.createWithBitmap(Bitmap.createBitmap(200, 200, Config.ARGB_8888)))
+ .build();
+ ShortcutInfo spied = spy(info);
+ doReturn(updateTime).when(spied).getLastChangedTimestamp();
+ return new CacheableShortcutInfo(spied,
+ new ApplicationInfoWrapper(getInstrumentation().getContext().getApplicationInfo()));
+ }
+
private ItemInfoWithIcon getBadgingInfo(Context context,
@Nullable ComponentName cn, @Nullable String badgeOverride) throws Exception {
Builder builder = new Builder(context, "test-shortcut")
diff --git a/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt b/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt
index e27926f..b54636c 100644
--- a/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt
@@ -25,6 +25,8 @@
import com.android.launcher3.icons.cache.BaseIconCache
import com.android.launcher3.icons.cache.CachingLogic
import com.android.launcher3.icons.cache.IconCacheUpdateHandler
+import com.android.launcher3.util.RoboApiWrapper
+import java.util.concurrent.FutureTask
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -51,7 +53,7 @@
System.currentTimeMillis(),
1,
1.0.toLong(),
- "stateOfConfusion"
+ "stateOfConfusion",
)
@Before
@@ -81,17 +83,32 @@
componentMap,
ignorePackages,
user,
- cachingLogic
+ cachingLogic,
)
assert(result == null)
}
}
+/** Utility method to wait for the icon update handler to finish */
+fun IconCache.waitForUpdateHandlerToFinish() {
+ var cacheUpdateInProgress = true
+ while (cacheUpdateInProgress) {
+ val cacheCheck = FutureTask {
+ // Check for pending message on the worker thread itself as some task may be
+ // running currently
+ workerHandler.hasMessages(0, iconUpdateToken)
+ }
+ workerHandler.postDelayed(cacheCheck, 10)
+ RoboApiWrapper.waitForLooperSync(workerHandler.looper)
+ cacheUpdateInProgress = cacheCheck.get()
+ }
+}
+
data class IconCacheRowData(
val component: String,
val lastUpdated: Long,
val version: Int,
val row: Long,
- val systemState: String
+ val systemState: String,
)
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt
index 371bac2..4ca47e3 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt
@@ -19,16 +19,15 @@
import androidx.test.filters.SmallTest
import com.android.launcher3.LauncherAppState
import com.android.launcher3.icons.BitmapInfo
+import com.android.launcher3.icons.waitForUpdateHandlerToFinish
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.util.Executors
import com.android.launcher3.util.LauncherLayoutBuilder
import com.android.launcher3.util.LauncherModelHelper
import com.android.launcher3.util.LauncherModelHelper.*
-import com.android.launcher3.util.RoboApiWrapper
import com.android.launcher3.util.TestUtil
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
-import java.util.concurrent.CountDownLatch
import org.junit.After
import org.junit.Before
import org.junit.Rule
@@ -147,14 +146,9 @@
// The first load initializes the DB, load again so that icons are now used from the DB
// Wait for the icon cache to be updated and then reload
val app = LauncherAppState.getInstance(modelHelper.sandboxContext)
- val cache = app.iconCache
- while (cache.isIconUpdateInProgress) {
- val wait = CountDownLatch(1)
- Executors.MODEL_EXECUTOR.handler.postDelayed({ wait.countDown() }, 10)
- RoboApiWrapper.waitForLooperSync(Executors.MODEL_EXECUTOR.handler.looper)
- wait.await()
- }
- TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) { cache.clearMemoryCache() }
+ app.iconCache.waitForUpdateHandlerToFinish()
+
+ TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) { app.iconCache.clearMemoryCache() }
// Reload again with correct icon state
app.model.forceReload()
modelHelper.loadModelSync()
diff --git a/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java b/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
index 7c87c65..44b8ff8 100644
--- a/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
+++ b/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
@@ -16,14 +16,11 @@
package com.android.launcher3.dragging;
import static com.android.launcher3.testing.shared.TestProtocol.ICON_MISSING;
-import static com.android.launcher3.testing.shared.TestProtocol.UIOBJECT_STALE_ELEMENT;
import static com.android.launcher3.util.TestConstants.AppNames.DUMMY_APP_NAME;
import static com.android.launcher3.util.TestConstants.AppNames.GMAIL_APP_NAME;
import static com.android.launcher3.util.TestConstants.AppNames.MAPS_APP_NAME;
import static com.android.launcher3.util.TestConstants.AppNames.STORE_APP_NAME;
import static com.android.launcher3.util.TestConstants.AppNames.TEST_APP_NAME;
-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;
@@ -40,7 +37,6 @@
import com.android.launcher3.util.TestUtil;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.ScreenRecordRule;
-import com.android.launcher3.util.rule.TestStabilityRule;
import org.junit.Test;
@@ -150,7 +146,6 @@
0, Math.min(gridPositions.length, appNameCandidates.length));
for (int i = 0; i < appNames.length; ++i) {
- Log.d(UIOBJECT_STALE_ELEMENT, "creatingShortcut for: " + appNames[i]);
createShortcutIfNotExist(appNames[i], gridPositions[i]);
}
diff --git a/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt b/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt
index ca2ef42..a991981 100644
--- a/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt
+++ b/tests/src/com/android/launcher3/pm/InstallSessionHelperTest.kt
@@ -28,6 +28,7 @@
import androidx.test.filters.SmallTest
import com.android.launcher3.LauncherPrefs
import com.android.launcher3.LauncherPrefs.Companion.PROMISE_ICON_IDS
+import com.android.launcher3.util.DaggerSingletonTracker
import com.android.launcher3.util.Executors.MODEL_EXECUTOR
import com.android.launcher3.util.IntArray
import com.android.launcher3.util.LauncherModelHelper
@@ -53,6 +54,7 @@
private val expectedAppPackage = "expectedAppPackage"
private val expectedInstallerPackage = "expectedInstallerPackage"
private val mockPackageInstaller: PackageInstaller = mock()
+ private val mTracker: DaggerSingletonTracker = mock()
private lateinit var installSessionHelper: InstallSessionHelper
private lateinit var launcherApps: LauncherApps
@@ -62,7 +64,7 @@
whenever(packageManager.packageInstaller).thenReturn(mockPackageInstaller)
whenever(sandboxContext.packageName).thenReturn(expectedInstallerPackage)
launcherApps = sandboxContext.spyService(LauncherApps::class.java)
- installSessionHelper = InstallSessionHelper(sandboxContext)
+ installSessionHelper = InstallSessionHelper(sandboxContext, mTracker)
}
@Test
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 68004bb..cee88ac 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -26,6 +26,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -103,6 +104,8 @@
public static final long DEFAULT_UI_TIMEOUT = TestUtil.DEFAULT_UI_TIMEOUT;
private static final String TAG = "AbstractLauncherUiTest";
+ private static final long BYTES_PER_MEGABYTE = 1 << 20;
+
private static boolean sDumpWasGenerated = false;
private static boolean sActivityLeakReported = false;
private static boolean sSeenKeyguard = false;
@@ -124,6 +127,10 @@
protected String mTargetPackage;
private int mLauncherPid;
+ private final ActivityManager.MemoryInfo mMemoryInfo = new ActivityManager.MemoryInfo();
+ private final ActivityManager mActivityManager;
+ private long mMemoryBefore;
+
/** Detects activity leaks and throws an exception if a leak is found. */
public static void checkDetectedLeaks(LauncherInstrumentation launcher) {
checkDetectedLeaks(launcher, false);
@@ -192,6 +199,8 @@
}
protected AbstractLauncherUiTest() {
+ mActivityManager = InstrumentationRegistry.getContext()
+ .getSystemService(ActivityManager.class);
mLauncher.enableCheckEventsForSuccessfulGestures();
mLauncher.setAnomalyChecker(AbstractLauncherUiTest::verifyKeyguardInvisible);
try {
@@ -311,6 +320,26 @@
initialize(this);
}
+ private long getAvailableMemory() {
+ mActivityManager.getMemoryInfo(mMemoryInfo);
+
+ return Math.divideExact(mMemoryInfo.availMem, BYTES_PER_MEGABYTE);
+ }
+
+ @Before
+ public void saveMemoryBefore() {
+ mMemoryBefore = getAvailableMemory();
+ }
+
+ @After
+ public void logMemoryAfter() {
+ long memoryAfter = getAvailableMemory();
+
+ Log.d(TAG, "Available memory: before=" + mMemoryBefore
+ + "MB, after=" + memoryAfter
+ + "MB, delta=" + (memoryAfter - mMemoryBefore) + "MB");
+ }
+
/** Method that should be called when a test starts. */
public static void onTestStart() {
waitForSetupWizardDismissal();
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index c8c5384..a29362f 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -25,7 +25,6 @@
import static com.android.launcher3.testing.shared.TestProtocol.ALL_APPS_STATE_ORDINAL;
import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
-import static com.android.launcher3.testing.shared.TestProtocol.UIOBJECT_STALE_ELEMENT;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertTrue;
@@ -33,7 +32,6 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.os.SystemClock;
-import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -370,14 +368,9 @@
.collect(
Collectors.toMap(
/* keyMapper= */ uiObject21 -> {
- Log.d(UIOBJECT_STALE_ELEMENT, "keyText: "
- + uiObject21.getText());
return uiObject21.getText();
},
/* valueMapper= */ uiObject2 -> {
- Log.d(UIOBJECT_STALE_ELEMENT, uiObject2.getText() +
- " dispId" + uiObject2.getDisplayId() +
- " parent" + uiObject2.getParent());
return uiObject2.getVisibleCenter();
},
/* mergeFunction= */ (p1, p2) -> p1.x < p2.x ? p1 : p2));