diff --git a/aconfig/launcher_overview.aconfig b/aconfig/launcher_overview.aconfig index 93d8d54c95..b299edf303 100644 --- a/aconfig/launcher_overview.aconfig +++ b/aconfig/launcher_overview.aconfig @@ -61,4 +61,14 @@ flag { namespace: "launcher_overview" description: "Enables the non-overlapping layout for desktop windows in Overview mode." bug: "378011776" +} + +flag { + name: "enable_use_top_visible_activity_for_exclude_from_recent_task" + namespace: "launcher_overview" + description: "Enables using the top visible activity for exclude from recent task instead of the activity indicies." + bug: "342627272" + metadata { + purpose: PURPOSE_BUGFIX + } } \ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java index fd0243a035..2ff9b18947 100644 --- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java +++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java @@ -111,10 +111,9 @@ public class DesktopVisibilityController { public boolean areDesktopTasksVisible() { boolean desktopTasksVisible = mVisibleDesktopTasksCount > 0; if (DEBUG) { - Log.d(TAG, "areDesktopTasksVisible: desktopVisible=" + desktopTasksVisible - + " overview=" + mInOverviewState); + Log.d(TAG, "areDesktopTasksVisible: desktopVisible=" + desktopTasksVisible); } - return desktopTasksVisible && !mInOverviewState; + return desktopTasksVisible; } /** @@ -219,12 +218,8 @@ public class DesktopVisibilityController { + " currentValue=" + mInOverviewState); } if (overviewStateEnabled != mInOverviewState) { - final boolean wereDesktopTasksVisibleBefore = areDesktopTasksVisible(); mInOverviewState = overviewStateEnabled; final boolean areDesktopTasksVisibleNow = areDesktopTasksVisible(); - if (wereDesktopTasksVisibleBefore != areDesktopTasksVisibleNow) { - notifyDesktopVisibilityListeners(areDesktopTasksVisibleNow); - } if (ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()) { return; diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 0f639f97ff..b416a10674 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -442,6 +442,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext { onNavButtonsDarkIntensityChanged(sharedState.navButtonsDarkIntensity); onNavigationBarLumaSamplingEnabled(sharedState.mLumaSamplingDisplayId, sharedState.mIsLumaSamplingEnabled); + setWallpaperVisible(sharedState.wallpaperVisible); + onTransitionModeUpdated(sharedState.barMode, true /* checkBarModes */); if (ENABLE_TASKBAR_NAVBAR_UNIFICATION) { // W/ the flag not set this entire class gets re-created, which resets the value of @@ -1285,9 +1287,25 @@ public class TaskbarActivityContext extends BaseTaskbarContext { } else if (tag instanceof TaskItemInfo info) { RemoteTransition remoteTransition = canUnminimizeDesktopTask(info.getTaskId()) ? createUnminimizeRemoteTransition() : null; - UI_HELPER_EXECUTOR.execute(() -> - SystemUiProxy.INSTANCE.get(this).showDesktopApp( - info.getTaskId(), remoteTransition)); + + if (areDesktopTasksVisible() && recents != null) { + TaskView taskView = recents.getTaskViewByTaskId(info.getTaskId()); + if (taskView == null) return; + RunnableList runnableList = taskView.launchWithAnimation(); + if (runnableList != null) { + runnableList.add(() -> + // wrapped it in runnable here since we need the post for DW to be + // ready. if we don't other DW will be gone and only the launched task + // will show. + UI_HELPER_EXECUTOR.execute(() -> + SystemUiProxy.INSTANCE.get(this).showDesktopApp( + info.getTaskId(), remoteTransition))); + } + } else { + UI_HELPER_EXECUTOR.execute(() -> + SystemUiProxy.INSTANCE.get(this).showDesktopApp( + info.getTaskId(), remoteTransition)); + } mControllers.taskbarStashController.updateAndAnimateTransientTaskbar( /* stash= */ true); } else if (tag instanceof WorkspaceItemInfo) { @@ -1531,7 +1549,17 @@ public class TaskbarActivityContext extends BaseTaskbarContext { .launchAppPair((AppPairIcon) launchingIconView, -1 /*cuj*/))); } else { - startItemInfoActivity(itemInfos.get(0), foundTask); + if (areDesktopTasksVisible()) { + RunnableList runnableList = recents.launchDesktopTaskView(); + // Wrapping it in runnable so we post after DW is ready for the app + // launch. + if (runnableList != null) { + runnableList.add(() -> UI_HELPER_EXECUTOR.execute( + () -> startItemInfoActivity(itemInfos.get(0), foundTask))); + } + } else { + startItemInfoActivity(itemInfos.get(0), foundTask); + } } } ); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index 2db796121e..250e33a56f 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -1018,7 +1018,12 @@ public class TaskbarLauncherStateController { @Override public void onRecentsAnimationFinished(RecentsAnimationController controller) { - endGestureStateOverride(!controller.getFinishTargetIsLauncher(), false /*canceled*/); + endGestureStateOverride(!controller.getFinishTargetIsLauncher(), + controller.getLauncherIsVisibleAtFinish(), false /*canceled*/); + } + + private void endGestureStateOverride(boolean finishedToApp, boolean canceled) { + endGestureStateOverride(finishedToApp, finishedToApp, canceled); } /** @@ -1028,11 +1033,13 @@ public class TaskbarLauncherStateController { * * @param finishedToApp {@code true} if the recents animation finished to showing an app and * not workspace or overview + * @param launcherIsVisible {code true} if launcher is visible at finish * @param canceled {@code true} if the recents animation was canceled instead of * finishing * to completion */ - private void endGestureStateOverride(boolean finishedToApp, boolean canceled) { + private void endGestureStateOverride(boolean finishedToApp, boolean launcherIsVisible, + boolean canceled) { mCallbacks.removeListener(this); mTaskBarRecentsAnimationListener = null; ((RecentsView) mLauncher.getOverviewPanel()).setTaskLaunchListener(null); @@ -1041,18 +1048,27 @@ public class TaskbarLauncherStateController { mSkipNextRecentsAnimEnd = false; return; } - updateStateForUserFinishedToApp(finishedToApp); + updateStateForUserFinishedToApp(finishedToApp, launcherIsVisible); } } + /** + * @see #updateStateForUserFinishedToApp(boolean, boolean) + */ + private void updateStateForUserFinishedToApp(boolean finishedToApp) { + updateStateForUserFinishedToApp(finishedToApp, !finishedToApp); + } + /** * Updates the visible state immediately to ensure a seamless handoff. * * @param finishedToApp True iff user is in an app. + * @param launcherIsVisible True iff launcher is still visible (ie. transparent app) */ - private void updateStateForUserFinishedToApp(boolean finishedToApp) { + private void updateStateForUserFinishedToApp(boolean finishedToApp, + boolean launcherIsVisible) { // Update the visible state immediately to ensure a seamless handoff - boolean launcherVisible = !finishedToApp; + boolean launcherVisible = !finishedToApp || launcherIsVisible; updateStateForFlag(FLAG_TRANSITION_TO_VISIBLE, false); updateStateForFlag(FLAG_VISIBLE, launcherVisible); applyState(); @@ -1061,7 +1077,7 @@ public class TaskbarLauncherStateController { if (DEBUG) { Log.d(TAG, "endGestureStateOverride - FLAG_IN_APP: " + finishedToApp); } - controller.updateStateForFlag(FLAG_IN_APP, finishedToApp); + controller.updateStateForFlag(FLAG_IN_APP, finishedToApp && !launcherIsVisible); controller.applyState(); } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java index 2e0bae5200..abf35a21b9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.taskbar; +import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS; import static com.android.launcher3.model.data.AppInfo.COMPONENT_KEY_COMPARATOR; import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition; @@ -309,9 +310,7 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba */ SystemShortcut.Factory createNewWindowShortcutFactory() { return (context, itemInfo, originalView) -> { - ComponentKey key = itemInfo.getComponentKey(); - AppInfo app = getApp(key); - if (app != null && app.supportsMultiInstance()) { + if (shouldShowMultiInstanceOptions(itemInfo)) { return new NewWindowTaskbarShortcut<>(context, itemInfo, originalView); } return null; @@ -325,9 +324,7 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba */ public SystemShortcut.Factory createManageWindowsShortcutFactory() { return (context, itemInfo, originalView) -> { - ComponentKey key = itemInfo.getComponentKey(); - AppInfo app = getApp(key); - if (app != null && app.supportsMultiInstance()) { + if (shouldShowMultiInstanceOptions(itemInfo)) { return new ManageWindowsTaskbarShortcut<>(context, itemInfo, originalView, mControllers); } @@ -335,6 +332,16 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba }; } + /** + * Determines whether to show multi-instance options for a given item. + */ + private boolean shouldShowMultiInstanceOptions(ItemInfo itemInfo) { + ComponentKey key = itemInfo.getComponentKey(); + AppInfo app = getApp(key); + return app != null && app.supportsMultiInstance() + && itemInfo.container != CONTAINER_ALL_APPS; + } + /** * A single menu item ("Split left," "Split right," or "Split top") that executes a split * from the taskbar, as if the user performed a drag and drop split. diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 19700142c0..fbc8d6a306 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -80,6 +80,8 @@ import android.os.IBinder; import android.os.SystemClock; import android.util.Log; import android.util.Pair; +import android.util.TimeUtils; +import android.view.Choreographer; import android.view.MotionEvent; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; @@ -1734,13 +1736,30 @@ public abstract class AbsSwipeUpHandler< } private void handOffAnimation(PointF velocityPxPerMs) { - if (!TransitionAnimator.Companion.longLivedReturnAnimationsEnabled() - || mRecentsAnimationController == null) { + if (!TransitionAnimator.Companion.longLivedReturnAnimationsEnabled()) { + return; + } + + // This function is not guaranteed to be called inside a frame. We try to access the frame + // time immediately, but if we're not inside a frame we must post a callback to be run at + // the beginning of the next frame. + try { + handOffAnimationInternal(Choreographer.getInstance().getFrameTime(), velocityPxPerMs); + } catch (IllegalStateException e) { + Choreographer.getInstance().postFrameCallback( + frameTimeNanos -> handOffAnimationInternal( + frameTimeNanos / TimeUtils.NANOS_PER_MS, velocityPxPerMs)); + } + } + + private void handOffAnimationInternal(long timestamp, PointF velocityPxPerMs) { + if (mRecentsAnimationController == null) { return; } Pair targetsAndStates = - extractTargetsAndStates(mRemoteTargetHandles, velocityPxPerMs); + extractTargetsAndStates( + mRemoteTargetHandles, timestamp, velocityPxPerMs); mRecentsAnimationController.handOffAnimation( targetsAndStates.first, targetsAndStates.second); ActiveGestureProtoLogProxy.logHandOffAnimation(); diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java index 055aadb41e..145773d0c2 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java @@ -56,6 +56,8 @@ public class RecentsAnimationController { private boolean mFinishRequested = false; // Only valid when mFinishRequested == true. private boolean mFinishTargetIsLauncher; + // Only valid when mFinishRequested == true + private boolean mLauncherIsVisibleAtFinish; private RunnableList mPendingFinishCallbacks = new RunnableList(); public RecentsAnimationController(RecentsAnimationControllerCompat controller, @@ -129,14 +131,28 @@ public class RecentsAnimationController { finishController(toRecents, onFinishComplete, sendUserLeaveHint); } + @UiThread + public void finish(boolean toRecents, boolean launcherIsVisibleAtFinish, + Runnable onFinishComplete, boolean sendUserLeaveHint) { + Preconditions.assertUIThread(); + finishController(toRecents, launcherIsVisibleAtFinish, onFinishComplete, sendUserLeaveHint, + false); + } + @UiThread public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint) { - finishController(toRecents, callback, sendUserLeaveHint, false /* forceFinish */); + finishController(toRecents, false, callback, sendUserLeaveHint, false /* forceFinish */); } @UiThread public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint, boolean forceFinish) { + finishController(toRecents, toRecents, callback, sendUserLeaveHint, forceFinish); + } + + @UiThread + public void finishController(boolean toRecents, boolean launcherIsVisibleAtFinish, + Runnable callback, boolean sendUserLeaveHint, boolean forceFinish) { mPendingFinishCallbacks.add(callback); if (!forceFinish && mFinishRequested) { // If finish has already been requested, then add the callback to the pending list. @@ -148,6 +164,7 @@ public class RecentsAnimationController { // Finish not yet requested mFinishRequested = true; mFinishTargetIsLauncher = toRecents; + mLauncherIsVisibleAtFinish = launcherIsVisibleAtFinish; mOnFinishedListener.accept(this); Runnable finishCb = () -> { mController.finish(toRecents, sendUserLeaveHint, new IResultReceiver.Stub() { @@ -224,6 +241,14 @@ public class RecentsAnimationController { return mFinishTargetIsLauncher; } + /** + * RecentsAnimationListeners can check this in onRecentsAnimationFinished() to determine whether + * the animation was finished to launcher vs an app. + */ + public boolean getLauncherIsVisibleAtFinish() { + return mLauncherIsVisibleAtFinish; + } + public void dump(String prefix, PrintWriter pw) { pw.println(prefix + "RecentsAnimationController:"); diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java index 783c87c8af..084cede033 100644 --- a/quickstep/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java @@ -795,14 +795,15 @@ public final class TaskViewUtils { * second applies to the target in the same index of the first. * * @param handles The handles wrapping each target. + * @param timestamp The start time of the current frame. * @param velocityPxPerMs The current velocity of the target animations. */ @NonNull public static Pair extractTargetsAndStates( - @NonNull RemoteTargetHandle[] handles, @NonNull PointF velocityPxPerMs) { + @NonNull RemoteTargetHandle[] handles, long timestamp, + @NonNull PointF velocityPxPerMs) { RemoteAnimationTarget[] targets = new RemoteAnimationTarget[handles.length]; WindowAnimationState[] animationStates = new WindowAnimationState[handles.length]; - long timestamp = System.currentTimeMillis(); for (int i = 0; i < handles.length; i++) { targets[i] = handles[i].getTransformParams().getTargetSet().apps[i]; diff --git a/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt index c3b072deac..908e9f96b7 100644 --- a/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt +++ b/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt @@ -48,16 +48,6 @@ class RecentsViewUtils { return otherTasks + desktopTasks } - /** Returns the expected index of the focus task. */ - fun getFocusedTaskIndex(taskGroups: List): Int { - // The focused task index is placed after the desktop tasks views. - return if (enableLargeDesktopWindowingTile()) { - taskGroups.count { it.taskViewType == TaskViewType.DESKTOP } - } else { - 0 - } - } - /** Counts [TaskView]s that are [DesktopTaskView] instances. */ fun getDesktopTaskViewCount(taskViews: Iterable): Int = taskViews.count { it is DesktopTaskView } @@ -81,6 +71,30 @@ class RecentsViewUtils { ): TaskView? = taskViews.firstOrNull { it.isLargeTile && !(splitSelectActive && it is DesktopTaskView) } + /** Returns the expected focus task. */ + fun getExpectedFocusedTask(taskViews: Iterable): TaskView? = + if (enableLargeDesktopWindowingTile()) taskViews.firstOrNull { it !is DesktopTaskView } + else taskViews.firstOrNull() + + /** + * Returns the [TaskView] that should be the current page during task binding, in the following + * priorities: + * 1. Running task + * 2. Focused task + * 3. First non-desktop task + * 4. Last desktop task + * 5. null otherwise + */ + fun getExpectedCurrentTask( + runningTaskView: TaskView?, + focusedTaskView: TaskView?, + taskViews: Iterable, + ): TaskView? = + runningTaskView + ?: focusedTaskView + ?: taskViews.firstOrNull { it !is DesktopTaskView } + ?: taskViews.lastOrNull() + /** * Returns the first TaskView that is not large * diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java index d35a36aaea..1eb91ae047 100644 --- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java +++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java @@ -106,6 +106,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.function.Consumer; /** @@ -941,6 +942,10 @@ public class SplitSelectStateController { mLauncher, mLauncher.getDragLayer(), null /* thumbnail */, mAppIcon, new RectF()); + floatingTaskView.setOnClickListener(view -> + getSplitAnimationController() + .playAnimPlaceholderToFullscreen(mContainer, view, + Optional.of(() -> resetState()))); floatingTaskView.setAlpha(1); floatingTaskView.addStagingAnimation(anim, mTaskBounds, mTempRect, false /* fadeWithThumbnail */, true /* isStagedTask */); diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 6fc33dc08a..e7cb05ea0b 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -1509,6 +1509,19 @@ public abstract class RecentsView< return focusedTask != null && isTaskInExpectedScrollPosition(indexOfChild(focusedTask)); } + /** + * Launch DesktopTaskView if found. + * @return provides runnable list to attach runnable at end of Desktop Mode launch + */ + public RunnableList launchDesktopTaskView() { + for (TaskView taskView : getTaskViews()) { + if (taskView instanceof DesktopTaskView) { + return taskView.launchWithAnimation(); + } + } + return null; + } + /** * Returns a {@link TaskView} that has taskId matching {@code taskId} or null if no match. */ @@ -1957,18 +1970,20 @@ public abstract class RecentsView< } // Keep same previous focused task - TaskView newFocusedTaskView = getTaskViewByTaskIds(focusedTaskIds); - if (enableLargeDesktopWindowingTile() && newFocusedTaskView instanceof DesktopTaskView) { - newFocusedTaskView = null; + TaskView newFocusedTaskView = null; + if (!enableGridOnlyOverview()) { + newFocusedTaskView = getTaskViewByTaskIds(focusedTaskIds); + if (enableLargeDesktopWindowingTile() + && newFocusedTaskView instanceof DesktopTaskView) { + newFocusedTaskView = null; + } + // If the list changed, maybe the focused task doesn't exist anymore. + if (newFocusedTaskView == null) { + newFocusedTaskView = mUtils.getExpectedFocusedTask(getTaskViews()); + } } - // If the list changed, maybe the focused task doesn't exist anymore - int newFocusedTaskViewIndex = mUtils.getFocusedTaskIndex(taskGroups); - if (newFocusedTaskView == null && getTaskViewCount() > newFocusedTaskViewIndex) { - newFocusedTaskView = getTaskViewAt(newFocusedTaskViewIndex); - } - - setFocusedTaskViewId(newFocusedTaskView != null && !enableGridOnlyOverview() - ? newFocusedTaskView.getTaskViewId() : INVALID_TASK_ID); + setFocusedTaskViewId( + newFocusedTaskView != null ? newFocusedTaskView.getTaskViewId() : INVALID_TASK_ID); updateTaskSize(); updateChildTaskOrientations(); @@ -1987,6 +2002,7 @@ public abstract class RecentsView< // for cases where the running task isn't included in this load plan (e.g. if // the current running task is excludedFromRecents.) showCurrentTask(mActiveGestureRunningTasks, "applyLoadPlan"); + newRunningTaskView = getRunningTaskView(); } else { setRunningTaskViewId(INVALID_TASK_ID); } @@ -2006,12 +2022,9 @@ public abstract class RecentsView< } else if (previousFocusedPage != INVALID_PAGE) { targetPage = previousFocusedPage; } else { - // Set the current page to the running task, but not if settling on new task. - if (hasAllValidTaskIds(runningTaskIds)) { - targetPage = indexOfChild(newRunningTaskView); - } else if (getTaskViewCount() > newFocusedTaskViewIndex) { - targetPage = indexOfChild(requireTaskViewAt(newFocusedTaskViewIndex)); - } + targetPage = indexOfChild( + mUtils.getExpectedCurrentTask(newRunningTaskView, newFocusedTaskView, + getTaskViews())); } if (targetPage != -1 && mCurrentPage != targetPage) { int finalTargetPage = targetPage; @@ -5887,12 +5900,19 @@ public abstract class RecentsView< finishRecentsAnimation(toRecents, true /* shouldPip */, onFinishComplete); } + /** + * Finish recents animation. + */ + public void finishRecentsAnimation(boolean toRecents, boolean shouldPip, + @Nullable Runnable onFinishComplete) { + finishRecentsAnimation(toRecents, shouldPip, false, onFinishComplete); + } /** * NOTE: Whatever value gets passed through to the toRecents param may need to also be set on * {@link #mRecentsAnimationController#setWillFinishToHome}. */ public void finishRecentsAnimation(boolean toRecents, boolean shouldPip, - @Nullable Runnable onFinishComplete) { + boolean allAppTargetsAreTranslucent, @Nullable Runnable onFinishComplete) { Log.d(TAG, "finishRecentsAnimation - mRecentsAnimationController: " + mRecentsAnimationController); // TODO(b/197232424#comment#10) Move this back into onRecentsAnimationComplete(). Maybe? @@ -5906,8 +5926,9 @@ public abstract class RecentsView< } final boolean sendUserLeaveHint = toRecents && shouldPip; - if (sendUserLeaveHint) { + if (sendUserLeaveHint && !com.android.wm.shell.Flags.enablePip2()) { // Notify the SysUI to use fade-in animation when entering PiP from live tile. + // Note: PiP2 handles entering differently, so skip if enable_pip2=true. final SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.get(getContext()); systemUiProxy.setPipAnimationTypeToAlpha(); systemUiProxy.setShelfHeight(true, mContainer.getDeviceProfile().hotseatBarSizePx); @@ -5924,7 +5945,7 @@ public abstract class RecentsView< tx, null /* overlay */); } } - mRecentsAnimationController.finish(toRecents, () -> { + mRecentsAnimationController.finish(toRecents, allAppTargetsAreTranslucent, () -> { if (onFinishComplete != null) { onFinishComplete.run(); } diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 16d9525b38..f1274dc74a 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -425,7 +425,9 @@ public class DeviceProfile { && WindowManagerProxy.INSTANCE.get(context).isTaskbarDrawnInProcess(); // Some more constants. - context = getContext(context, info, isVerticalBarLayout() || (isTablet && isLandscape) + context = getContext(context, info, inv.isFixedLandscape + || isVerticalBarLayout() + || (isTablet && isLandscape) ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT, windowBounds); diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index 6be809869c..b20d8a5152 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -36,6 +36,7 @@ import android.widget.FrameLayout; import androidx.annotation.IntDef; import androidx.annotation.Nullable; +import com.android.launcher3.celllayout.CellLayoutLayoutParams; import com.android.launcher3.util.HorizontalInsettableView; import com.android.launcher3.util.MultiPropertyFactory; import com.android.launcher3.util.MultiPropertyFactory.MultiProperty; @@ -201,13 +202,16 @@ public class Hotseat extends CellLayout implements Insettable { AnimatorSet animatorSet = new AnimatorSet(); for (int i = 0; i < icons.getChildCount(); i++) { View child = icons.getChildAt(i); - float tx = shouldAdjustHotseat ? dp.getHotseatAdjustedTranslation(getContext(), i) : 0; - if (child instanceof Reorderable) { - MultiTranslateDelegate mtd = ((Reorderable) child).getTranslateDelegate(); - animatorSet.play( - mtd.getTranslationX(INDEX_BUBBLE_ADJUSTMENT_ANIM).animateToValue(tx)); - } else { - animatorSet.play(ObjectAnimator.ofFloat(child, VIEW_TRANSLATE_X, tx)); + if (child.getLayoutParams() instanceof CellLayoutLayoutParams lp) { + float tx = shouldAdjustHotseat + ? dp.getHotseatAdjustedTranslation(getContext(), lp.getCellX()) : 0; + if (child instanceof Reorderable) { + MultiTranslateDelegate mtd = ((Reorderable) child).getTranslateDelegate(); + animatorSet.play( + mtd.getTranslationX(INDEX_BUBBLE_ADJUSTMENT_ANIM).animateToValue(tx)); + } else { + animatorSet.play(ObjectAnimator.ofFloat(child, VIEW_TRANSLATE_X, tx)); + } } } //TODO(b/381109832) refactor & simplify adjustment logic diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index dfbcf5c439..5becddeed7 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -63,6 +63,7 @@ import com.android.launcher3.util.Partner; import com.android.launcher3.util.ResourceHelper; import com.android.launcher3.util.SafeCloseable; import com.android.launcher3.util.WindowBounds; +import com.android.launcher3.util.window.CachedDisplayInfo; import com.android.launcher3.util.window.WindowManagerProxy; import org.xmlpull.v1.XmlPullParser; @@ -240,10 +241,7 @@ public class InvariantDeviceProfile implements SafeCloseable { @TargetApi(23) private InvariantDeviceProfile(Context context) { String gridName = getCurrentGridName(context); - String newGridName = initGrid(context, gridName); - if (!newGridName.equals(gridName)) { - LauncherPrefs.get(context).put(GRID_NAME, newGridName); - } + initGrid(context, gridName); DisplayController.INSTANCE.get(context).setPriorityListener( (displayContext, info, flags) -> { @@ -367,6 +365,11 @@ public class InvariantDeviceProfile implements SafeCloseable { ? new ArrayList<>(allOptions) : new ArrayList<>(allOptionsFilteredByColCount), displayInfo.getDeviceType()); + + if (!displayOption.grid.name.equals(gridName)) { + LauncherPrefs.get(context).put(GRID_NAME, displayOption.grid.name); + } + initGrid(context, displayInfo, displayOption); return displayOption.grid.name; } @@ -679,28 +682,15 @@ public class InvariantDeviceProfile implements SafeCloseable { } private static int[] findMinWidthAndHeightDpForDevice(Info displayInfo) { - int minWidthPx = Integer.MAX_VALUE; - int minHeightPx = Integer.MAX_VALUE; - for (WindowBounds bounds : displayInfo.supportedBounds) { - boolean isTablet = displayInfo.isTablet(bounds); - if (isTablet && displayInfo.getDeviceType() == TYPE_MULTI_DISPLAY) { - // For split displays, take half width per page. - minWidthPx = Math.min(minWidthPx, bounds.availableSize.x / 2); - minHeightPx = Math.min(minHeightPx, bounds.availableSize.y); - } else if (!isTablet && bounds.isLandscape()) { - // We will use transposed layout in this case. - minWidthPx = Math.min(minWidthPx, bounds.availableSize.y); - minHeightPx = Math.min(minHeightPx, bounds.availableSize.x); - } else { - minWidthPx = Math.min(minWidthPx, bounds.availableSize.x); - minHeightPx = Math.min(minHeightPx, bounds.availableSize.y); - } + int minDisplayWidthDp = Integer.MAX_VALUE; + int minDisplayHeightDp = Integer.MAX_VALUE; + for (CachedDisplayInfo display: displayInfo.getAllDisplays()) { + minDisplayWidthDp = Math.min(minDisplayWidthDp, + (int) dpiFromPx(display.size.x, DisplayMetrics.DENSITY_DEVICE_STABLE)); + minDisplayHeightDp = Math.min(minDisplayHeightDp, + (int) dpiFromPx(display.size.y, DisplayMetrics.DENSITY_DEVICE_STABLE)); } - - int minWidthDp = (int) dpiFromPx(minWidthPx, DisplayMetrics.DENSITY_DEVICE_STABLE); - int minHeightDp = (int) dpiFromPx(minHeightPx, DisplayMetrics.DENSITY_DEVICE_STABLE); - - return new int[]{minWidthDp, minHeightDp}; + return new int[]{minDisplayWidthDp, minDisplayHeightDp}; } /** diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 83eace83c6..f96e9591e6 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -389,7 +389,7 @@ public class LoaderTask implements Runnable { } } catch (CancellationException e) { // Loader stopped, ignore - logASplit("Cancelled"); + FileLog.w(TAG, "LoaderTask cancelled:", e); } catch (Exception e) { memoryLogger.printLogs(); throw e; @@ -398,6 +398,7 @@ public class LoaderTask implements Runnable { } public synchronized void stopLocked() { + FileLog.w(TAG, "LoaderTask#stopLocked:", new Exception()); mStopped = true; this.notify(); } diff --git a/tests/Launcher3Tests.xml b/tests/Launcher3Tests.xml index 270a610814..a8768609b0 100644 --- a/tests/Launcher3Tests.xml +++ b/tests/Launcher3Tests.xml @@ -48,6 +48,8 @@