From 20eb0e3f67d649dcdc9ce6a6b135089fb874691f Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Thu, 9 Jun 2022 17:40:07 +0000 Subject: [PATCH 01/10] Replace shelf height with keep clear areas registration in Launcher. This affects Hotseat only for now. Taskbar will be occluded when unstashed. When in gesture nav and with auto-enter pip, we need to pre-register the Hotseat keep clear areas, as otherwise the event appears after the animator is started and current logic doesn't allow to update those destination bounds in the animator. Test: manually, existing tests pass Bug: 183746978 Change-Id: I4d97ca77225d3502acac1fb6b5e3eff3e81285ed --- .../uioverrides/QuickstepLauncher.java | 26 +++++++++------ .../android/quickstep/AbsSwipeUpHandler.java | 33 ++++++++++++++++++- .../com/android/quickstep/SystemUiProxy.java | 6 ++-- res/layout/hotseat.xml | 1 + 4 files changed, 52 insertions(+), 14 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 0f3ea15b80..c9bc260a21 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -64,6 +64,7 @@ import android.hardware.devicestate.DeviceStateManager; import android.os.Bundle; import android.os.CancellationSignal; import android.os.IBinder; +import android.os.SystemProperties; import android.view.Display; import android.view.HapticFeedbackConstants; import android.view.View; @@ -159,6 +160,9 @@ import java.util.stream.Stream; public class QuickstepLauncher extends Launcher { + public static final boolean ENABLE_PIP_KEEP_CLEAR_ALGORITHM = + SystemProperties.getBoolean("persist.wm.debug.enable_pip_keep_clear_algorithm", false); + public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false; /** * Reusable command for applying the shelf height on the background thread. @@ -349,16 +353,18 @@ public class QuickstepLauncher extends Launcher { */ private void onStateOrResumeChanging(boolean inTransition) { LauncherState state = getStateManager().getState(); - boolean started = ((getActivityFlags() & ACTIVITY_STATE_STARTED)) != 0; - if (started) { - DeviceProfile profile = getDeviceProfile(); - boolean willUserBeActive = - (getActivityFlags() & ACTIVITY_STATE_USER_WILL_BE_ACTIVE) != 0; - boolean visible = (state == NORMAL || state == OVERVIEW) - && (willUserBeActive || isUserActive()) - && !profile.isVerticalBarLayout(); - UiThreadHelper.runAsyncCommand(this, SET_SHELF_HEIGHT, visible ? 1 : 0, - profile.hotseatBarSizePx); + if (!ENABLE_PIP_KEEP_CLEAR_ALGORITHM) { + boolean started = ((getActivityFlags() & ACTIVITY_STATE_STARTED)) != 0; + if (started) { + DeviceProfile profile = getDeviceProfile(); + boolean willUserBeActive = + (getActivityFlags() & ACTIVITY_STATE_USER_WILL_BE_ACTIVE) != 0; + boolean visible = (state == NORMAL || state == OVERVIEW) + && (willUserBeActive || isUserActive()) + && !profile.isVerticalBarLayout(); + UiThreadHelper.runAsyncCommand(this, SET_SHELF_HEIGHT, visible ? 1 : 0, + profile.hotseatBarSizePx); + } } if (state == NORMAL && !inTransition) { ((RecentsView) getOverviewPanel()).setSwipeDownShouldLaunchApp(false); diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 8dee10aa53..7592237b86 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -33,6 +33,7 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_GESTURE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_LEFT; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT; +import static com.android.launcher3.uioverrides.QuickstepLauncher.ENABLE_PIP_KEEP_CLEAR_ALGORITHM; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK; @@ -1420,12 +1421,13 @@ public abstract class AbsSwipeUpHandler, homeToWindowPositionMap.invert(windowToHomePositionMap); windowToHomePositionMap.mapRect(startRect); + final Rect hotseatKeepClearArea = getKeepClearAreaForHotseat(); final Rect destinationBounds = SystemUiProxy.INSTANCE.get(mContext) .startSwipePipToHome(taskInfo.topActivity, taskInfo.topActivityInfo, runningTaskTarget.taskInfo.pictureInPictureParams, homeRotation, - mDp.hotseatBarSizePx); + hotseatKeepClearArea); final Rect appBounds = new Rect(); final WindowConfiguration winConfig = taskInfo.configuration.windowConfiguration; // Adjust the appBounds for TaskBar by using the calculated window crop Rect @@ -1488,6 +1490,35 @@ public abstract class AbsSwipeUpHandler, return swipePipToHomeAnimator; } + private Rect getKeepClearAreaForHotseat() { + Rect keepClearArea; + if (!ENABLE_PIP_KEEP_CLEAR_ALGORITHM) { + // make the height equal to hotseatBarSizePx only + keepClearArea = new Rect(0, 0, mDp.hotseatBarSizePx, 0); + return keepClearArea; + } + // the keep clear area in global screen coordinates, in pixels + if (mDp.isPhone) { + if (mDp.isSeascape()) { + // in seascape the Hotseat is on the left edge of the screen + keepClearArea = new Rect(0, 0, mDp.hotseatBarSizePx, mDp.heightPx); + } else if (mDp.isLandscape) { + // in landscape the Hotseat is on the right edge of the screen + keepClearArea = new Rect(mDp.widthPx - mDp.hotseatBarSizePx, 0, + mDp.widthPx, mDp.heightPx); + } else { + // in portrait mode the Hotseat is at the bottom of the screen + keepClearArea = new Rect(0, mDp.heightPx - mDp.hotseatBarSizePx, + mDp.widthPx, mDp.heightPx); + } + } else { + // large screens have Hotseat always at the bottom of the screen + keepClearArea = new Rect(0, mDp.heightPx - mDp.hotseatBarSizePx, + mDp.widthPx, mDp.heightPx); + } + return keepClearArea; + } + private void startInterceptingTouchesForGesture() { if (mRecentsAnimationController == null) { return; diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index 0ec7e628b5..f1d15aa3e6 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -28,7 +28,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; -import android.graphics.Bitmap; import android.graphics.Insets; import android.graphics.Rect; import android.os.Bundle; @@ -509,11 +508,12 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI } public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, - PictureInPictureParams pictureInPictureParams, int launcherRotation, int shelfHeight) { + PictureInPictureParams pictureInPictureParams, int launcherRotation, + Rect hotseatKeepClearArea) { if (mPip != null) { try { return mPip.startSwipePipToHome(componentName, activityInfo, - pictureInPictureParams, launcherRotation, shelfHeight); + pictureInPictureParams, launcherRotation, hotseatKeepClearArea); } catch (RemoteException e) { Log.w(TAG, "Failed call startSwipePipToHome", e); } diff --git a/res/layout/hotseat.xml b/res/layout/hotseat.xml index 82b0b8d74e..95ebd94d0e 100644 --- a/res/layout/hotseat.xml +++ b/res/layout/hotseat.xml @@ -21,4 +21,5 @@ android:layout_height="match_parent" android:theme="@style/HomeScreenElementTheme" android:importantForAccessibility="no" + android:preferKeepClear="true" launcher:containerType="hotseat" /> \ No newline at end of file From b368fcc624e6d145624f70292f4a643b6fa3185d Mon Sep 17 00:00:00 2001 From: Anushree Ganjam Date: Mon, 29 Aug 2022 18:40:57 +0000 Subject: [PATCH 02/10] Add a boolean to track QSB edu card dismissal. Bug: 243680092 Test: Manual. Change-Id: I6981e423d037851020ae1cdec9879ab577769258 --- src/com/android/launcher3/util/OnboardingPrefs.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java index f4cf21efe5..d942b7a8a5 100644 --- a/src/com/android/launcher3/util/OnboardingPrefs.java +++ b/src/com/android/launcher3/util/OnboardingPrefs.java @@ -43,13 +43,14 @@ public class OnboardingPrefs { public static final String SEARCH_ONBOARDING_COUNT = "launcher.search_onboarding_count"; public static final String TASKBAR_EDU_SEEN = "launcher.taskbar_edu_seen"; public static final String ALL_APPS_VISITED_COUNT = "launcher.all_apps_visited_count"; + public static final String QSB_SEARCH_ONBOARDING_CARD_DISMISSED = "launcher.qsb_edu_dismiss"; // When adding a new key, add it here as well, to be able to reset it from Developer Options. public static final Map ALL_PREF_KEYS = Map.of( "All Apps Bounce", new String[] { HOME_BOUNCE_SEEN, HOME_BOUNCE_COUNT }, "Hybrid Hotseat Education", new String[] { HOTSEAT_DISCOVERY_TIP_COUNT, HOTSEAT_LONGPRESS_TIP_SEEN }, "Search Education", new String[] { SEARCH_KEYBOARD_EDU_SEEN, SEARCH_SNACKBAR_COUNT, - SEARCH_ONBOARDING_COUNT}, + SEARCH_ONBOARDING_COUNT, QSB_SEARCH_ONBOARDING_CARD_DISMISSED}, "Taskbar Education", new String[] { TASKBAR_EDU_SEEN }, "All Apps Visited Count", new String[] {ALL_APPS_VISITED_COUNT} ); @@ -61,7 +62,8 @@ public class OnboardingPrefs { HOME_BOUNCE_SEEN, HOTSEAT_LONGPRESS_TIP_SEEN, SEARCH_KEYBOARD_EDU_SEEN, - TASKBAR_EDU_SEEN + TASKBAR_EDU_SEEN, + QSB_SEARCH_ONBOARDING_CARD_DISMISSED }) @Retention(RetentionPolicy.SOURCE) public @interface EventBoolKey {} From 10c1c017c909405fc08762892655a7311d3de065 Mon Sep 17 00:00:00 2001 From: Evan Rosky Date: Mon, 29 Aug 2022 21:35:56 +0000 Subject: [PATCH 03/10] Only animate to hotseat when launcher is on home screen Was assuming that resume == home-screen; however, in shell transitions, launcher is resumed while overview is active, so make sure it is both resumed AND "isTaskbarAligned" before making the to-hotseat animation Bug: 241800590 Test: Open an app, long-press taskbar to stash, long-press again to unstash Change-Id: I117afcb006c363e50205f27f014ffc30d6f2896a --- .../launcher3/taskbar/LauncherTaskbarUIController.java | 8 ++++++++ .../launcher3/uioverrides/states/BackgroundAppState.java | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java index 6c740bac50..b7ac079ee2 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -16,6 +16,7 @@ package com.android.launcher3.taskbar; import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_RESUMED; +import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS; import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR; import android.animation.Animator; @@ -188,6 +189,13 @@ public class LauncherTaskbarUIController extends TaskbarUIController { } } + if (ENABLE_SHELL_TRANSITIONS + && !mLauncher.getStateManager().getState().isTaskbarAlignedWithHotseat(mLauncher)) { + // Launcher is resumed, but in a state where taskbar is still independent, so + // ignore the state change. + return null; + } + mTaskbarLauncherStateController.updateStateForFlag(FLAG_RESUMED, isResumed); return mTaskbarLauncherStateController.applyState(fromInit ? 0 : duration, startAnimation); } diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java index 2eade67cc1..4150d40bea 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java @@ -16,6 +16,7 @@ package com.android.launcher3.uioverrides.states; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; +import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS; import android.content.Context; import android.graphics.Color; @@ -101,6 +102,12 @@ public class BackgroundAppState extends OverviewState { return Color.TRANSPARENT; } + @Override + public boolean isTaskbarAlignedWithHotseat(Launcher launcher) { + if (ENABLE_SHELL_TRANSITIONS) return false; + return super.isTaskbarAlignedWithHotseat(launcher); + } + public static float[] getOverviewScaleAndOffsetForBackgroundState( BaseDraggingActivity activity) { return new float[] { From 314bbf1cbab04b9a2ba937aa293c0e86e89bde84 Mon Sep 17 00:00:00 2001 From: Sebastian Franco Date: Mon, 13 Jun 2022 14:38:43 -0700 Subject: [PATCH 04/10] Adding support to add icons in the workspace for tests Test: atest ReorderWidgets Bug: 243440737 Change-Id: Ic656cef079be965d17ab1b58d5f73ce955c9374c --- .../launcher3/celllayout/CellLayoutBoard.java | 126 ++++++++++++++---- .../launcher3/celllayout/ReorderWidgets.java | 81 ++++------- .../celllayout/TestWorkspaceBuilder.java | 110 +++++++++++++++ .../celllayout/testcases/FullReorderCase.java | 4 + .../testcases/MoveOutReorderCase.java | 4 + .../celllayout/testcases/PushReorderCase.java | 4 + .../testcases/SimpleReorderCase.java | 4 + .../launcher3/ui/AbstractLauncherUiTest.java | 2 +- 8 files changed, 253 insertions(+), 82 deletions(-) create mode 100644 tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java index 3ca05bce80..a32ce3c558 100644 --- a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java +++ b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java @@ -30,39 +30,117 @@ import java.util.Set; public class CellLayoutBoard { + public static class CellType { + // The cells marked by this will be filled by 1x1 widgets and will be ignored when + // validating + public static final char IGNORE = 'x'; + // The cells marked by this will be filled by app icons + public static final char ICON = 'i'; + // Empty space + public static final char EMPTY = '-'; + // Widget that will be saved as "main widget" for easier retrieval + public static final char MAIN_WIDGET = 'm'; + // Everything else will be consider a widget + } + + public static class WidgetRect { + public char mType; + public Rect mBounds; + + WidgetRect(char type, Rect bounds) { + this.mType = type; + this.mBounds = bounds; + } + + int getSpanX() { + return mBounds.right - mBounds.left + 1; + } + + int getSpanY() { + return mBounds.top - mBounds.bottom + 1; + } + + int getCellX() { + return mBounds.left; + } + + int getCellY() { + return mBounds.bottom; + } + + boolean shouldIgnore() { + return this.mType == CellType.IGNORE; + } + + @Override + public String toString() { + return "WidgetRect type = " + mType + " bounds = " + mBounds.toString(); + } + } + + public static class IconPoint { + public Point coord; + public char mType; + + public IconPoint(Point coord, char type) { + this.coord = coord; + mType = type; + } + + public char getType() { + return mType; + } + + public void setType(char type) { + mType = type; + } + + public Point getCoord() { + return coord; + } + + public void setCoord(Point coord) { + this.coord = coord; + } + } + static final int INFINITE = 99999; - char[][] mBoard = new char[30][30]; + char[][] mWidget = new char[30][30]; - List mWidgetsRects = new ArrayList<>(); - Map mWidgetsMap = new HashMap<>(); + List mWidgetsRects = new ArrayList<>(); + Map mWidgetsMap = new HashMap<>(); - List mIconPoints = new ArrayList<>(); - Map mIconsMap = new HashMap<>(); + List mIconPoints = new ArrayList<>(); + Map mIconsMap = new HashMap<>(); Point mMain = new Point(); CellLayoutBoard() { - for (int x = 0; x < mBoard.length; x++) { - for (int y = 0; y < mBoard[0].length; y++) { - mBoard[x][y] = '-'; + for (int x = 0; x < mWidget.length; x++) { + for (int y = 0; y < mWidget[0].length; y++) { + mWidget[x][y] = CellType.EMPTY; } } } - public List getWidgets() { + public List getWidgets() { return mWidgetsRects; } + public List getIcons() { + return mIconPoints; + } + public Point getMain() { return mMain; } - public TestBoardWidget getWidgetRect(char c) { + public WidgetRect getWidgetRect(char c) { return mWidgetsMap.get(c); } - public static TestBoardWidget getWidgetRect(int x, int y, Set used, char[][] board) { + public static WidgetRect getWidgetRect(int x, int y, Set used, char[][] board) { char type = board[x][y]; Queue search = new ArrayDeque(); Point current = new Point(x, y); @@ -91,20 +169,20 @@ public class CellLayoutBoard { } } } - return new TestBoardWidget(type, widgetRect); + return new WidgetRect(type, widgetRect); } public static boolean isWidget(char type) { - return type != 'i' && type != '-'; + return type != CellType.ICON && type != CellType.EMPTY; } public static boolean isIcon(char type) { - return type == 'i'; + return type == CellType.ICON; } - private static List getRects(char[][] board) { + private static List getRects(char[][] board) { Set used = new HashSet<>(); - List widgetsRects = new ArrayList<>(); + List widgetsRects = new ArrayList<>(); for (int x = 0; x < board.length; x++) { for (int y = 0; y < board[0].length; y++) { if (!used.contains(new Point(x, y)) && isWidget(board[x][y])) { @@ -115,12 +193,12 @@ public class CellLayoutBoard { return widgetsRects; } - private static List getIconPoints(char[][] board) { - List iconPoints = new ArrayList<>(); + private static List getIconPoints(char[][] board) { + List iconPoints = new ArrayList<>(); for (int x = 0; x < board.length; x++) { for (int y = 0; y < board[0].length; y++) { if (isIcon(board[x][y])) { - iconPoints.add(new TestBoardAppIcon(new Point(x, y), board[x][y])); + iconPoints.add(new IconPoint(new Point(x, y), board[x][y])); } } } @@ -135,18 +213,18 @@ public class CellLayoutBoard { String line = lines[y]; for (int x = 0; x < line.length(); x++) { char c = line.charAt(x); - if (c == 'm') { + if (c == CellType.MAIN_WIDGET) { board.mMain = new Point(x, y); } - if (c != '-') { - board.mBoard[x][y] = line.charAt(x); + if (c != CellType.EMPTY) { + board.mWidget[x][y] = line.charAt(x); } } } - board.mWidgetsRects = getRects(board.mBoard); + board.mWidgetsRects = getRects(board.mWidget); board.mWidgetsRects.forEach( widgetRect -> board.mWidgetsMap.put(widgetRect.mType, widgetRect)); - board.mIconPoints = getIconPoints(board.mBoard); + board.mIconPoints = getIconPoints(board.mWidget); return board; } } diff --git a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java index 5f5dfea2c3..93fbf97e99 100644 --- a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java +++ b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java @@ -15,16 +15,13 @@ */ package com.android.launcher3.celllayout; -import static com.android.launcher3.util.WidgetUtils.createWidgetInfo; - +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import android.graphics.Point; -import android.graphics.Rect; import android.util.Log; import android.view.View; -import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -34,13 +31,14 @@ import com.android.launcher3.celllayout.testcases.MoveOutReorderCase; import com.android.launcher3.celllayout.testcases.PushReorderCase; import com.android.launcher3.celllayout.testcases.ReorderTestCase; import com.android.launcher3.celllayout.testcases.SimpleReorderCase; -import com.android.launcher3.model.data.LauncherAppWidgetInfo; +import com.android.launcher3.tapl.Widget; +import com.android.launcher3.tapl.WidgetResizeFrame; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.TaplTestsLauncher3; -import com.android.launcher3.ui.TestViewHelpers; import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord; import com.android.launcher3.util.rule.ShellCommandRule; -import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; +import com.android.launcher3.views.DoubleShadowBubbleTextView; +import com.android.launcher3.widget.LauncherAppWidgetHostView; import org.junit.Assume; import org.junit.Before; @@ -60,6 +58,8 @@ public class ReorderWidgets extends AbstractLauncherUiTest { private static final String TAG = ReorderWidgets.class.getSimpleName(); + TestWorkspaceBuilder mBoardBuilder; + private View getViewAt(int cellX, int cellY) { return getFromLauncher(l -> l.getWorkspace().getScreenWithId( l.getWorkspace().getScreenIdForPageIndex(0)).getChildAt(cellX, cellY)); @@ -76,6 +76,7 @@ public class ReorderWidgets extends AbstractLauncherUiTest { @Before public void setup() throws Throwable { + mBoardBuilder = new TestWorkspaceBuilder(this); TaplTestsLauncher3.initialize(this); clearHomescreen(); } @@ -86,78 +87,44 @@ public class ReorderWidgets extends AbstractLauncherUiTest { private boolean validateBoard(CellLayoutBoard board) { boolean match = true; Point cellDimensions = getCellDimensions(); - for (TestBoardWidget widgetRect: board.getWidgets()) { + for (CellLayoutBoard.WidgetRect widgetRect: board.getWidgets()) { if (widgetRect.shouldIgnore()) { continue; } View widget = getViewAt(widgetRect.getCellX(), widgetRect.getCellY()); + assertTrue("The view selected at " + board + " is not a widget", + widget instanceof LauncherAppWidgetHostView); match &= widgetRect.getSpanX() == Math.round(widget.getWidth() / (float) cellDimensions.x); match &= widgetRect.getSpanY() == Math.round(widget.getHeight() / (float) cellDimensions.y); if (!match) return match; } + for (CellLayoutBoard.IconPoint iconPoint : board.getIcons()) { + View icon = getViewAt(iconPoint.getCoord().x, iconPoint.getCoord().y); + assertTrue("The view selected at " + iconPoint.coord + " is not an Icon", + icon instanceof DoubleShadowBubbleTextView); + } return match; } - /** - * Fills the given rect in WidgetRect with 1x1 widgets. This is useful to equalize cases. - */ - private void fillWithWidgets(TestBoardWidget widgetRect) { - int initX = widgetRect.getCellX(); - int initY = widgetRect.getCellY(); - for (int x = 0; x < widgetRect.getSpanX(); x++) { - for (int y = 0; y < widgetRect.getSpanY(); y++) { - int auxX = initX + x; - int auxY = initY + y; - try { - // this widgets are filling, we don't care if we can't place them - addWidgetInCell( - new TestBoardWidget('x', - new Rect(auxX, auxY, auxX, auxY)) - ); - } catch (Exception e) { - Log.d(TAG, "Unable to place filling widget at " + auxX + "," + auxY); - } - } - } - } - - private void addWidgetInCell(TestBoardWidget widgetRect) { - LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false); - LauncherAppWidgetInfo item = createWidgetInfo(info, - ApplicationProvider.getApplicationContext(), true); - item.cellX = widgetRect.getCellX(); - item.cellY = widgetRect.getCellY(); - - item.spanX = widgetRect.getSpanX(); - item.spanY = widgetRect.getSpanY(); - addItemToScreen(item); - } - - private void addCorrespondingWidgetRect(TestBoardWidget widgetRect) { - if (widgetRect.mType == 'x') { - fillWithWidgets(widgetRect); - } else { - addWidgetInCell(widgetRect); - } - } - private void runTestCase(ReorderTestCase testCase) { Point mainWidgetCellPos = testCase.mStart.getMain(); - testCase.mStart.getWidgets().forEach(this::addCorrespondingWidgetRect); + mBoardBuilder.buildBoard(testCase.mStart); - mLauncher.getWorkspace() - .getWidgetAtCell(mainWidgetCellPos.x, mainWidgetCellPos.y) - .dragWidgetToWorkspace(testCase.moveMainTo.x, testCase.moveMainTo.y) - .dismiss(); // dismiss resize frame + Widget widget = mLauncher.getWorkspace().getWidgetAtCell(mainWidgetCellPos.x, + mainWidgetCellPos.y); + assertNotNull(widget); + WidgetResizeFrame resizeFrame = widget.dragWidgetToWorkspace(testCase.moveMainTo.x, + testCase.moveMainTo.y); + resizeFrame.dismiss(); boolean isValid = false; for (CellLayoutBoard board : testCase.mEnd) { isValid |= validateBoard(board); } - assertTrue("None of the valid boards match with the current state", isValid); + assertTrue("Non of the valid boards match with the current state", isValid); } /** diff --git a/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java b/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java new file mode 100644 index 0000000000..10e399dfd5 --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.celllayout; + +import static com.android.launcher3.util.WidgetUtils.createWidgetInfo; + +import android.content.ComponentName; +import android.graphics.Rect; +import android.os.Process; +import android.os.UserHandle; +import android.util.Log; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.model.data.LauncherAppWidgetInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.ui.AbstractLauncherUiTest; +import com.android.launcher3.ui.TestViewHelpers; +import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; + +public class TestWorkspaceBuilder { + + private static final ComponentName APP_COMPONENT_NAME = new ComponentName( + "com.google.android.calculator", "com.android.calculator2.Calculator"); + + public AbstractLauncherUiTest mTest; + + private UserHandle mMyUser; + + public TestWorkspaceBuilder(AbstractLauncherUiTest test) { + mTest = test; + mMyUser = Process.myUserHandle(); + } + + private static final String TAG = "CellLayoutBoardBuilder"; + + /** + * Fills the given rect in WidgetRect with 1x1 widgets. This is useful to equalize cases. + */ + private void fillWithWidgets(CellLayoutBoard.WidgetRect widgetRect) { + int initX = widgetRect.getCellX(); + int initY = widgetRect.getCellY(); + for (int x = initX; x < initX + widgetRect.getSpanX(); x++) { + for (int y = initY; y < initY + widgetRect.getSpanY(); y++) { + try { + // this widgets are filling, we don't care if we can't place them + addWidgetInCell( + new CellLayoutBoard.WidgetRect(CellLayoutBoard.CellType.IGNORE, + new Rect(x, y, x, y)) + ); + } catch (Exception e) { + Log.d(TAG, "Unable to place filling widget at " + x + "," + y); + } + } + } + } + + private void addWidgetInCell(CellLayoutBoard.WidgetRect widgetRect) { + LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(mTest, false); + LauncherAppWidgetInfo item = createWidgetInfo(info, + ApplicationProvider.getApplicationContext(), true); + + item.cellX = widgetRect.getCellX(); + item.cellY = widgetRect.getCellY(); + item.spanX = widgetRect.getSpanX(); + item.spanY = widgetRect.getSpanY(); + mTest.addItemToScreen(item); + } + + private void addIconInCell(CellLayoutBoard.IconPoint iconPoint) { + AppInfo appInfo = new AppInfo(APP_COMPONENT_NAME, "test icon", mMyUser, + AppInfo.makeLaunchIntent(APP_COMPONENT_NAME)); + + appInfo.cellX = iconPoint.getCoord().x; + appInfo.cellY = iconPoint.getCoord().y; + appInfo.minSpanY = appInfo.minSpanX = appInfo.spanX = appInfo.spanY = 1; + appInfo.container = LauncherSettings.Favorites.CONTAINER_DESKTOP; + appInfo.componentName = APP_COMPONENT_NAME; + + mTest.addItemToScreen(new WorkspaceItemInfo(appInfo)); + } + + private void addCorrespondingWidgetRect(CellLayoutBoard.WidgetRect widgetRect) { + if (widgetRect.mType == 'x') { + fillWithWidgets(widgetRect); + } else { + addWidgetInCell(widgetRect); + } + } + + public void buildBoard(CellLayoutBoard board) { + board.getWidgets().forEach(this::addCorrespondingWidgetRect); + board.getIcons().forEach(this::addIconInCell); + } +} diff --git a/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java index 1326389ab7..94e55cfbde 100644 --- a/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java +++ b/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java @@ -19,6 +19,10 @@ import android.graphics.Point; import java.util.Map; +/** + * The grids represent the workspace to be build by TestWorkspaceBuilder, to see what each character + * in the board mean refer to {@code CellType} + */ public class FullReorderCase { /** 5x5 Test diff --git a/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java index 1701390268..a222d3d551 100644 --- a/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java +++ b/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java @@ -19,6 +19,10 @@ import android.graphics.Point; import java.util.Map; +/** + * The grids represent the workspace to be build by TestWorkspaceBuilder, to see what each character + * in the board mean refer to {@code CellType} + */ public class MoveOutReorderCase { /** 5x5 Test diff --git a/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java index bb8d5e9631..e16ff42a8b 100644 --- a/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java +++ b/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java @@ -19,6 +19,10 @@ import android.graphics.Point; import java.util.Map; +/** + * The grids represent the workspace to be build by TestWorkspaceBuilder, to see what each character + * in the board mean refer to {@code CellType} + */ public class PushReorderCase { /** 5x5 Test diff --git a/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java index 30269a0f5d..546c48bccb 100644 --- a/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java +++ b/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java @@ -19,6 +19,10 @@ import android.graphics.Point; import java.util.Map; +/** + * The grids represent the workspace to be build by TestWorkspaceBuilder, to see what each character + * in the board mean refer to {@code CellType} + */ public class SimpleReorderCase { /** 5x5 Test diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 304153f53f..a66b09a87d 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -319,7 +319,7 @@ public abstract class AbstractLauncherUiTest { /** * Adds {@param item} on the homescreen on the 0th screen */ - protected void addItemToScreen(ItemInfo item) { + public void addItemToScreen(ItemInfo item) { WidgetUtils.addItemToScreen(item, mTargetContext); resetLoaderState(); From 4391c8e1ef7353deb8c8587ad06e7d1b694b5db2 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 31 Aug 2022 19:25:41 -0700 Subject: [PATCH 05/10] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I6446b8f18bf292261a6da1507f38d6a4c4935a74 --- res/values-pl/strings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index 448a56bcc1..86699499a5 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -106,13 +106,13 @@ "Funkcja wyłączona przez administratora" "Zezwalaj na obrót ekranu głównego" "Po obróceniu telefonu" - "Plakietki z powiadomieniami" + "Kropki powiadomień" "Włączono" "Wyłączono" "Wymagany jest dostęp do powiadomień" - "Aby pokazać plakietki z powiadomieniami, włącz powiadomienia aplikacji %1$s" + "Aby pokazywać kropki powiadomień, włącz powiadomienia aplikacji %1$s" "Zmień ustawienia" - "Pokaż plakietki z powiadomieniami" + "Pokaż kropki powiadomień" "Opcje programisty" "Dodawaj ikony aplikacji do ekranu głównego" "W przypadku nowych aplikacji" From 03caf49826e8d745853c76a4f77cca9c969e1156 Mon Sep 17 00:00:00 2001 From: Holly Sun Date: Fri, 19 Aug 2022 17:58:27 -0700 Subject: [PATCH 06/10] Update `container` for ItemInfo. See https://docs.google.com/document/d/1eAxQ9p263FI8-ZdkOsiQoarhpwm9aOdncqfJUD42o1Q/edit?resourcekey=0-GJyyDut-Tfy29akzHyoz0Q for the debugging process. Test: see video https://drive.google.com/file/d/1qpD7070v0qOaBEuYS0WoZauawyhL54MQ/view Fix: 234848972 Change-Id: I2ed5d24d11ab2081c6faa5d040d72a9d1646e5ca --- .../model/QuickstepModelDelegate.java | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java index 770dfb2e07..de0b14d4fa 100644 --- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java +++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java @@ -117,14 +117,15 @@ public class QuickstepModelDelegate extends ModelDelegate { // TODO: Implement caching and preloading super.loadItems(ums, pinnedShortcuts); - WorkspaceItemFactory allAppsFactory = new WorkspaceItemFactory( - mApp, ums, pinnedShortcuts, mIDP.numDatabaseAllAppsColumns); - FixedContainerItems allAppsItems = new FixedContainerItems(mAllAppsState.containerId, - mAllAppsState.storage.read(mApp.getContext(), allAppsFactory, ums.allUsers::get)); - mDataModel.extraItems.put(mAllAppsState.containerId, allAppsItems); + WorkspaceItemFactory allAppsFactory = new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, + mIDP.numDatabaseAllAppsColumns, mAllAppsState.containerId); + FixedContainerItems allAppsPredictionItems = new FixedContainerItems( + mAllAppsState.containerId, mAllAppsState.storage.read(mApp.getContext(), + allAppsFactory, ums.allUsers::get)); + mDataModel.extraItems.put(mAllAppsState.containerId, allAppsPredictionItems); - WorkspaceItemFactory hotseatFactory = - new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numDatabaseHotseatIcons); + WorkspaceItemFactory hotseatFactory = new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, + mIDP.numDatabaseHotseatIcons, mHotseatState.containerId); FixedContainerItems hotseatItems = new FixedContainerItems(mHotseatState.containerId, mHotseatState.storage.read(mApp.getContext(), hotseatFactory, ums.allUsers::get)); mDataModel.extraItems.put(mHotseatState.containerId, hotseatItems); @@ -432,15 +433,17 @@ public class QuickstepModelDelegate extends ModelDelegate { private final UserManagerState mUMS; private final Map mPinnedShortcuts; private final int mMaxCount; + private final int mContainer; private int mReadCount = 0; protected WorkspaceItemFactory(LauncherAppState appState, UserManagerState ums, - Map pinnedShortcuts, int maxCount) { + Map pinnedShortcuts, int maxCount, int container) { mAppState = appState; mUMS = ums; mPinnedShortcuts = pinnedShortcuts; mMaxCount = maxCount; + mContainer = container; } @Nullable @@ -458,6 +461,7 @@ public class QuickstepModelDelegate extends ModelDelegate { return null; } AppInfo info = new AppInfo(lai, user, mUMS.isUserQuiet(user)); + info.container = mContainer; mAppState.getIconCache().getTitleAndIcon(info, lai, false); mReadCount++; return info.makeWorkspaceItem(mAppState.getContext()); @@ -472,6 +476,7 @@ public class QuickstepModelDelegate extends ModelDelegate { return null; } WorkspaceItemInfo wii = new WorkspaceItemInfo(si, mAppState.getContext()); + wii.container = mContainer; mAppState.getIconCache().getShortcutIcon(wii, si); mReadCount++; return wii; From 3193522616f4cd1d56d22e88d94a49d4978f7329 Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Thu, 1 Sep 2022 21:24:52 +0100 Subject: [PATCH 07/10] Change wallpaper depth value in AllApps bottomsheet - Cap AllApps wallpaper zoom to workspaceContentScale rather than all the way to max depth (config_wallpaperMaxScale) - Changed both workspace scale and depth interpolator to correlate with AllApps threshold Bug: 240580498 Test: manual Change-Id: I0342a37c72206268dcffc5697a212704a41b020f --- .../allapps/TaskbarAllAppsSlideInView.java | 7 ++--- .../uioverrides/states/AllAppsState.java | 30 ++++++++++++++----- .../com/android/quickstep/TaskViewUtils.java | 5 ++-- src/com/android/launcher3/LauncherState.java | 9 ++++-- .../android/launcher3/anim/Interpolators.java | 15 +++++++++- .../touch/AllAppsSwipeController.java | 9 ++++-- .../uioverrides/states/AllAppsState.java | 6 ++-- 7 files changed, 59 insertions(+), 22 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java index c4837a0c51..0e62da3f63 100644 --- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java +++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java @@ -16,8 +16,7 @@ package com.android.launcher3.taskbar.allapps; import static com.android.launcher3.LauncherState.ALL_APPS; -import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE; -import static com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE; +import static com.android.launcher3.anim.Interpolators.EMPHASIZED; import android.animation.PropertyValuesHolder; import android.content.Context; @@ -60,7 +59,7 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView int getTransitionDuration(DEVICE_PROFILE_CONTEXT context, boolean isToState) { - return !context.getDeviceProfile().isTablet && isToState - ? 600 - : isToState ? 500 : 300; + return context.getDeviceProfile().isTablet + ? 500 + : isToState ? 600 : 300; } @Override @@ -77,10 +80,23 @@ public class AllAppsState extends LauncherState { } @Override - protected float getDepthUnchecked(Context context) { - // The scrim fades in at approximately 50% of the swipe gesture. - // This means that the depth should be greater than 1, in order to fully zoom out. - return 2f; + protected + float getDepthUnchecked(DEVICE_PROFILE_CONTEXT context) { + if (context.getDeviceProfile().isTablet) { + // The goal is to set wallpaper to zoom at workspaceContentScale when in AllApps. + // When depth is 0, wallpaper zoom is set to maxWallpaperScale. + // When depth is 1, wallpaper zoom is set to 1. + // For depth to achieve zoom set to maxWallpaperScale * workspaceContentScale: + float maxWallpaperScale = context.getResources().getFloat( + com.android.internal.R.dimen.config_wallpaperMaxScale); + return Utilities.mapToRange( + maxWallpaperScale * context.getDeviceProfile().workspaceContentScale, + maxWallpaperScale, 1f, 0f, 1f, LINEAR); + } else { + // The scrim fades in at approximately 50% of the swipe gesture. + // This means that the depth should be greater than 1, in order to fully zoom out. + return 2f; + } } @Override diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java index a809c9c09b..93170cb79b 100644 --- a/quickstep/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java @@ -195,7 +195,8 @@ public final class TaskViewUtils { int taskIndex = recentsView.indexOfChild(v); Context context = v.getContext(); - DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile(); + BaseActivity baseActivity = BaseActivity.fromContext(context); + DeviceProfile dp = baseActivity.getDeviceProfile(); boolean showAsGrid = dp.isTablet; boolean parallaxCenterAndAdjacentTask = taskIndex != recentsView.getCurrentPage() && !showAsGrid; @@ -368,7 +369,7 @@ public final class TaskViewUtils { }); if (depthController != null) { - out.setFloat(depthController, DEPTH, BACKGROUND_APP.getDepth(context), + out.setFloat(depthController, DEPTH, BACKGROUND_APP.getDepth(baseActivity), TOUCH_RESPONSE_INTERPOLATOR); } } diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index 4532ed4b05..c3b5392cf5 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -265,7 +265,8 @@ public abstract class LauncherState implements BaseState { * * 0 means completely zoomed in, without blurs. 1 is zoomed out, with blurs. */ - public final float getDepth(Context context) { + public final + float getDepth(DEVICE_PROFILE_CONTEXT context) { return getDepth(context, BaseDraggingActivity.fromContext(context).getDeviceProfile().isMultiWindowMode); } @@ -275,14 +276,16 @@ public abstract class LauncherState implements BaseState { * * @see #getDepth(Context). */ - public final float getDepth(Context context, boolean isMultiWindowMode) { + public final + float getDepth(DEVICE_PROFILE_CONTEXT context, boolean isMultiWindowMode) { if (isMultiWindowMode) { return 0; } return getDepthUnchecked(context); } - protected float getDepthUnchecked(Context context) { + protected + float getDepthUnchecked(DEVICE_PROFILE_CONTEXT context) { return 0f; } diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java index 0a77aa7bcf..12b4223bac 100644 --- a/src/com/android/launcher3/anim/Interpolators.java +++ b/src/com/android/launcher3/anim/Interpolators.java @@ -57,6 +57,10 @@ public class Interpolators { public static final Interpolator DECELERATED_EASE = new PathInterpolator(0, 0, .2f, 1f); public static final Interpolator ACCELERATED_EASE = new PathInterpolator(0.4f, 0, 1f, 1f); + /** + * The default emphasized interpolator. Used for hero / emphasized movement of content. + */ + public static final Interpolator EMPHASIZED = createEmphasizedInterpolator(); public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator( 0.3f, 0f, 0.8f, 0.15f); public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator( @@ -87,7 +91,6 @@ public class Interpolators { public static final Interpolator TOUCH_RESPONSE_INTERPOLATOR_ACCEL_DEACCEL = v -> ACCEL_DEACCEL.getInterpolation(TOUCH_RESPONSE_INTERPOLATOR.getInterpolation(v)); - /** * Inversion of ZOOM_OUT, compounded with an ease-out. */ @@ -218,4 +221,14 @@ public class Interpolators { public static Interpolator reverse(Interpolator interpolator) { return t -> 1 - interpolator.getInterpolation(1 - t); } + + // Create the default emphasized interpolator + private static PathInterpolator createEmphasizedInterpolator() { + Path path = new Path(); + // Doing the same as fast_out_extra_slow_in + path.moveTo(0f, 0f); + path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f); + path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f); + return new PathInterpolator(path); + } } diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java index 37b76fb90c..5279dec73b 100644 --- a/src/com/android/launcher3/touch/AllAppsSwipeController.java +++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java @@ -18,6 +18,7 @@ package com.android.launcher3.touch; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.anim.Interpolators.DECELERATED_EASE; +import static com.android.launcher3.anim.Interpolators.EMPHASIZED; import static com.android.launcher3.anim.Interpolators.EMPHASIZED_ACCELERATE; import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE; import static com.android.launcher3.anim.Interpolators.FINAL_FRAME; @@ -199,8 +200,10 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController { Interpolators.reverse(ALL_APPS_SCRIM_RESPONDER)); config.setInterpolator(ANIM_ALL_APPS_FADE, FINAL_FRAME); if (!config.userControlled) { - config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED_ACCELERATE); + config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED); } + config.setInterpolator(ANIM_WORKSPACE_SCALE, EMPHASIZED); + config.setInterpolator(ANIM_DEPTH, EMPHASIZED); } else { if (config.userControlled) { config.setInterpolator(ANIM_DEPTH, Interpolators.reverse(BLUR_MANUAL)); @@ -238,8 +241,10 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController { config.setInterpolator(ANIM_ALL_APPS_FADE, INSTANT); config.setInterpolator(ANIM_SCRIM_FADE, ALL_APPS_SCRIM_RESPONDER); if (!config.userControlled) { - config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED_DECELERATE); + config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED); } + config.setInterpolator(ANIM_WORKSPACE_SCALE, EMPHASIZED); + config.setInterpolator(ANIM_DEPTH, EMPHASIZED); } else { config.setInterpolator(ANIM_DEPTH, config.userControlled ? BLUR_MANUAL : BLUR_ATOMIC); config.setInterpolator(ANIM_WORKSPACE_FADE, diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java index bf35dd8d08..9daea940af 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java @@ -32,7 +32,6 @@ import com.android.launcher3.util.Themes; public class AllAppsState extends LauncherState { private static final float PARALLAX_COEFFICIENT = .125f; - private static final float WORKSPACE_SCALE_FACTOR = 0.97f; private static final int STATE_FLAGS = FLAG_WORKSPACE_INACCESSIBLE; @@ -60,7 +59,8 @@ public class AllAppsState extends LauncherState { @Override public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) { - return new ScaleAndTranslation(WORKSPACE_SCALE_FACTOR, NO_OFFSET, NO_OFFSET); + return new ScaleAndTranslation(launcher.getDeviceProfile().workspaceContentScale, NO_OFFSET, + NO_OFFSET); } @Override @@ -71,7 +71,7 @@ public class AllAppsState extends LauncherState { ScaleAndTranslation overviewScaleAndTranslation = LauncherState.OVERVIEW .getWorkspaceScaleAndTranslation(launcher); return new ScaleAndTranslation( - WORKSPACE_SCALE_FACTOR, + launcher.getDeviceProfile().workspaceContentScale, overviewScaleAndTranslation.translationX, overviewScaleAndTranslation.translationY); } From b33471a21f41931791f556104cda01ddcde3d302 Mon Sep 17 00:00:00 2001 From: Jeremy Sim Date: Wed, 17 Aug 2022 14:19:03 -0400 Subject: [PATCH 08/10] Fix bug with Launcher animation canceling, esp. around OverviewSplitSelect This commit fixes a bug where the user could cancel animations when transitioning between Launcher states, potentially resulting in a state where Overview elements (task thumbnails etc.) were wrongly hidden or invisible. The bug occurred because functions such as createInitialSplitSelectAnimation() and createAtomicAnimation() did not carry out any cleanup upon animation failure. This resulted in RecentsView potentially being in a polluted state for the next launch. Bug was fixed by adding cleanup routines to two onAnimationEnd listeners. Fixes: 242715097 Test: Manual Change-Id: I05415ecf515e247aa535e3ca8371e540c3189b01 --- .../BaseRecentsViewStateController.java | 18 +++---- .../fallback/FallbackRecentsView.java | 5 ++ .../quickstep/views/FloatingTaskView.java | 24 +++++++++ .../quickstep/views/GroupedTaskView.java | 6 +++ .../quickstep/views/LauncherRecentsView.java | 5 ++ .../android/quickstep/views/RecentsView.java | 50 ++++++------------- .../com/android/quickstep/views/TaskView.java | 17 +++++++ .../launcher3/statemanager/StateManager.java | 16 ++++++ 8 files changed, 97 insertions(+), 44 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java index 8782ee65a5..2e4e7397aa 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java @@ -28,11 +28,10 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SP import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y; import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW; +import static com.android.quickstep.views.FloatingTaskView.PRIMARY_TRANSLATE_OFFSCREEN; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET; -import static com.android.quickstep.views.RecentsView.FIRST_FLOATING_TASK_TRANSLATE_OFFSCREEN; import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS; import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; -import static com.android.quickstep.views.RecentsView.SPLIT_INSTRUCTIONS_FADE; import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION; import static com.android.quickstep.views.RecentsView.TASK_THUMBNAIL_SPLASH_ALPHA; @@ -112,6 +111,7 @@ public abstract class BaseRecentsViewStateController // TODO (b/238651489): Refactor state management to avoid need for double check FloatingTaskView floatingTask = mRecentsView.getFirstFloatingTaskView(); if (floatingTask != null) { + // We are in split selection state currently, transitioning to another state DragLayer dragLayer = mLauncher.getDragLayer(); RectF onScreenRectF = new RectF(); Utilities.getBoundsForViewInDragLayer(mLauncher.getDragLayer(), floatingTask, @@ -127,8 +127,8 @@ public abstract class BaseRecentsViewStateController ); setter.setFloat( - mRecentsView, - FIRST_FLOATING_TASK_TRANSLATE_OFFSCREEN, + mRecentsView.getFirstFloatingTaskView(), + PRIMARY_TRANSLATE_OFFSCREEN, mRecentsView.getPagedOrientationHandler() .getFloatingTaskOffscreenTranslationTarget( floatingTask, @@ -140,14 +140,14 @@ public abstract class BaseRecentsViewStateController ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN, LINEAR )); - setter.setFloat( - mRecentsView, - SPLIT_INSTRUCTIONS_FADE, - 1, + setter.setViewAlpha( + mRecentsView.getSplitInstructionsView(), + 0, config.getInterpolator( ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE, LINEAR - )); + ) + ); } } diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java index eb739a6972..7c96bf8a78 100644 --- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java +++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java @@ -226,6 +226,11 @@ public class FallbackRecentsView extends RecentsView PRIMARY_TRANSLATE_OFFSCREEN = + new FloatProperty("floatingTaskPrimaryTranslateOffscreen") { + @Override + public void setValue(FloatingTaskView view, float translation) { + ((RecentsView) view.mActivity.getOverviewPanel()).getPagedOrientationHandler() + .setFloatingTaskPrimaryTranslation( + view, + translation, + view.mActivity.getDeviceProfile() + ); + } + + @Override + public Float get(FloatingTaskView view) { + return ((RecentsView) view.mActivity.getOverviewPanel()) + .getPagedOrientationHandler() + .getFloatingTaskPrimaryTranslation( + view, + view.mActivity.getDeviceProfile() + ); + } + }; + private FloatingTaskThumbnailView mThumbnailView; private SplitPlaceholderView mSplitPlaceholderView; private RectF mStartingPosition; diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java index a870f9cc8f..2539ed6d26 100644 --- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java +++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java @@ -319,4 +319,10 @@ public class GroupedTaskView extends TaskView { super.applyThumbnailSplashAlpha(); mSnapshotView2.setSplashAlpha(mTaskThumbnailSplashAlpha); } + + @Override + void setThumbnailVisibility(int visibility) { + super.setThumbnailVisibility(visibility); + mSnapshotView2.setVisibility(visibility); + } } diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java index 6a33d36ab4..de7ccad7a3 100644 --- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java @@ -119,6 +119,11 @@ public class LauncherRecentsView extends RecentsView FIRST_FLOATING_TASK_TRANSLATE_OFFSCREEN = - new FloatProperty("firstFloatingTaskTranslateOffscreen") { - @Override - public void setValue(RecentsView view, float translation) { - view.getPagedOrientationHandler().setFloatingTaskPrimaryTranslation( - view.mFirstFloatingTaskView, - translation, - view.mActivity.getDeviceProfile() - ); - } - - @Override - public Float get(RecentsView view) { - return view.getPagedOrientationHandler().getFloatingTaskPrimaryTranslation( - view.mFirstFloatingTaskView, - view.mActivity.getDeviceProfile() - ); - } - }; - - public static final FloatProperty SPLIT_INSTRUCTIONS_FADE = - new FloatProperty("splitInstructionsFade") { - @Override - public void setValue(RecentsView view, float fade) { - view.mSplitInstructionsView.setAlpha(1 - fade); - } - - @Override - public Float get(RecentsView view) { - return 1 - view.mSplitInstructionsView.getAlpha(); - } - }; - // OverScroll constants private static final int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270; @@ -2827,7 +2794,11 @@ public abstract class RecentsView ICON_ALPHA = + new FloatProperty("iconAlpha") { + @Override + public void setValue(TaskView taskView, float v) { + taskView.mIconView.setAlpha(v); + } + + @Override + public Float get(TaskView taskView) { + return taskView.mIconView.getAlpha(); + } + }; + @Nullable protected Task mTask; protected TaskThumbnailView mSnapshotView; @@ -1488,6 +1501,10 @@ public class TaskView extends FrameLayout implements Reusable { return display != null ? display.getDisplayId() : DEFAULT_DISPLAY; } + void setThumbnailVisibility(int visibility) { + mSnapshotView.setVisibility(visibility); + } + /** * We update and subsequently draw these in {@link #setFullscreenProgress(float)}. */ diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java index 2aa9ddeb50..c44e1e1784 100644 --- a/src/com/android/launcher3/statemanager/StateManager.java +++ b/src/com/android/launcher3/statemanager/StateManager.java @@ -342,6 +342,11 @@ public class StateManager> { public void onAnimationSuccess(Animator animator) { onStateTransitionEnd(state); } + + @Override + public void onAnimationCancel(Animator animation) { + onStateTransitionFailed(state); + } }; } @@ -354,6 +359,12 @@ public class StateManager> { } } + private void onStateTransitionFailed(STATE_TYPE state) { + for (int i = mListeners.size() - 1; i >= 0; i--) { + mListeners.get(i).onStateTransitionFailed(state); + } + } + private void onStateTransitionEnd(STATE_TYPE state) { // Only change the stable states after the transitions have finished if (state != mCurrentStableState) { @@ -588,6 +599,11 @@ public class StateManager> { default void onStateTransitionStart(STATE_TYPE toState) { } + /** + * If the state transition animation fails (e.g. is canceled by the user), this fires. + */ + default void onStateTransitionFailed(STATE_TYPE toState) { } + default void onStateTransitionComplete(STATE_TYPE finalState) { } } From bf9ce9c0962b9f4c4f6d8384fc8bbec44982c2f1 Mon Sep 17 00:00:00 2001 From: Jerry Chang Date: Thu, 1 Sep 2022 13:39:41 +0000 Subject: [PATCH 09/10] Support launching a shortcut and a task to split screen Fix: 243101552 Test: long press on a shortcut to enter split screen works Change-Id: Icaabf2e1c8be086bd1b79e0a2cf05878767caa15 --- .../com/android/quickstep/SystemUiProxy.java | 17 +++++++- .../util/SplitSelectStateController.java | 43 +++++++++++++++++-- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index f1d15aa3e6..8e9ff2faa8 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -28,6 +28,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; +import android.content.pm.ShortcutInfo; import android.graphics.Insets; import android.graphics.Rect; import android.os.Bundle; @@ -602,7 +603,21 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent, taskId, mainOptions, sideOptions, sidePosition, splitRatio, adapter); } catch (RemoteException e) { - Log.w(TAG, "Failed call startTasksWithLegacyTransition"); + Log.w(TAG, "Failed call startIntentAndTaskWithLegacyTransition"); + } + } + } + + public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, int taskId, + Bundle mainOptions, Bundle sideOptions, + @SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio, + RemoteAnimationAdapter adapter) { + if (mSystemUiProxy != null) { + try { + mSplitScreen.startShortcutAndTaskWithLegacyTransition(shortcutInfo, taskId, + mainOptions, sideOptions, sidePosition, splitRatio, adapter); + } catch (RemoteException e) { + Log.w(TAG, "Failed call startShortcutAndTaskWithLegacyTransition"); } } } diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java index 8f322140b8..7efb1a51ad 100644 --- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java +++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java @@ -31,16 +31,20 @@ import android.app.ActivityThread; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ShortcutInfo; import android.os.Handler; import android.os.IBinder; import android.os.UserHandle; import android.text.TextUtils; +import android.util.Log; import android.view.RemoteAnimationAdapter; import android.view.SurfaceControl; import android.window.TransitionInfo; import androidx.annotation.Nullable; +import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.testing.TestLogging; @@ -66,6 +70,7 @@ import java.util.function.Consumer; * and is in the process of either a) selecting a second app or b) exiting intention to invoke split */ public class SplitSelectStateController { + private static final String TAG = "SplitSelectStateCtor"; private final Context mContext; private final Handler mHandler; @@ -196,7 +201,7 @@ public class SplitSelectStateController { null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, splitRatio, new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR, ActivityThread.currentActivityThread().getApplicationThread())); - // TODO: handle intent + task with shell transition + // TODO(b/237635859): handle intent/shortcut + task with shell transition } else { RemoteSplitLaunchAnimationRunner animationRunner = new RemoteSplitLaunchAnimationRunner(taskId1, taskPendingIntent, taskId2, @@ -215,9 +220,17 @@ public class SplitSelectStateController { taskIds[1], null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, splitRatio, adapter); } else { - mSystemUiProxy.startIntentAndTaskWithLegacyTransition(taskPendingIntent, - fillInIntent, taskId2, mainOpts.toBundle(), null /* sideOptions */, - stagePosition, splitRatio, adapter); + final ShortcutInfo shortcutInfo = getShortcutInfo(mInitialTaskIntent, + taskPendingIntent.getCreatorUserHandle()); + if (shortcutInfo != null) { + mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(shortcutInfo, taskId2, + mainOpts.toBundle(), null /* sideOptions */, stagePosition, splitRatio, + adapter); + } else { + mSystemUiProxy.startIntentAndTaskWithLegacyTransition(taskPendingIntent, + fillInIntent, taskId2, mainOpts.toBundle(), null /* sideOptions */, + stagePosition, splitRatio, adapter); + } } } } @@ -230,6 +243,28 @@ public class SplitSelectStateController { this.mRecentsAnimationRunning = running; } + @Nullable + private ShortcutInfo getShortcutInfo(Intent intent, UserHandle userHandle) { + if (intent == null || intent.getPackage() == null) { + return null; + } + + final String shortcutId = intent.getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID); + if (shortcutId == null) { + return null; + } + + try { + final Context context = mContext.createPackageContextAsUser( + intent.getPackage(), 0 /* flags */, userHandle); + return new ShortcutInfo.Builder(context, shortcutId).build(); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "Failed to create a ShortcutInfo for " + intent.getPackage()); + } + + return null; + } + /** * Requires Shell Transitions */ From 668eec1e08bf5f4ebc835cc50588e17e06313dea Mon Sep 17 00:00:00 2001 From: Tracy Zhou Date: Wed, 31 Aug 2022 19:52:24 -0700 Subject: [PATCH 10/10] Do not change layering for non live tile tasks rendering TaskViewSimulator#onBuildTargetParams is called for every single task rendering. Given the layering change doesn't work for the non live tile case (and we shouldn't change the layering in that case anyways), an additional check is added to prevent that from happening Fixes: 242593058 Test: launch overview from home and app, and make sure that in both cases, task launch animation is intact Change-Id: Ia242dc767499689547dfa8acae56d39e9b0c3189 --- .../src/com/android/quickstep/util/TaskViewSimulator.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java index d37300c7eb..0a49008225 100644 --- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java +++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java @@ -102,7 +102,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { private boolean mLayoutValid = false; private int mOrientationStateId; private SplitBounds mSplitBounds; - private boolean mDrawsBelowRecents; + private Boolean mDrawsBelowRecents = null; private boolean mIsGridTask; private int mTaskRectTranslationX; private int mTaskRectTranslationY; @@ -391,7 +391,8 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { .withWindowCrop(mTmpCropRect) .withCornerRadius(getCurrentCornerRadius()); - if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { + // If mDrawsBelowRecents is unset, no reordering will be enforced. + if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mDrawsBelowRecents != null) { // In legacy transitions, the animation leashes remain in same hierarchy in the // TaskDisplayArea, so we don't want to bump the layer too high otherwise it will // conflict with layers that WM core positions (ie. the input consumers). For shell