From 32828ecefd74dcee46745e11a700cb46b94ce63b Mon Sep 17 00:00:00 2001 From: Federico Baron Date: Wed, 30 Nov 2022 16:40:16 -0800 Subject: [PATCH 001/469] Set the ENABLE_DOWNLOAD_APP_UX_V2 flag to true We will split the feature into two parts. The part that is guarded under ENABLE_DOWNLOAD_APP_UX_V2 is feature complete (everything except for the skeleton motion). The skeleton motion will be guarded by another flag (ENABLE_DOWNLOAD_APP_UX_V3). This is so that we have an improvement over the current version of restore icon treatment prior to release that we can start testing Bug: 254858049 Test: Download an app and check how it looks when pending/donwloading/installing Change-Id: I711f9d8acdb40689e71782acff6ef43907bb7435 --- src/com/android/launcher3/config/FeatureFlags.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 3e1c0eb165..a3e71818c1 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -334,8 +334,8 @@ public final class FeatureFlags { "HOME_GARDENING_WORKSPACE_BUTTONS", false, "Change workspace edit buttons to reflect home gardening"); - public static final BooleanFlag ENABLE_DOWNLOAD_APP_UX_V2 = getDebugFlag( - "ENABLE_DOWNLOAD_APP_UX_V2", false, "Updates the download app UX" + public static final BooleanFlag ENABLE_DOWNLOAD_APP_UX_V2 = new DeviceFlag( + "ENABLE_DOWNLOAD_APP_UX_V2", true, "Updates the download app UX" + " to have better visuals"); public static final BooleanFlag ENABLE_TASKBAR_REVISED_THRESHOLDS = getDebugFlag( From 0d7dc94d2ed41cdd3ddf0af9adc0f1d5df046bea Mon Sep 17 00:00:00 2001 From: Tony Huang Date: Fri, 16 Dec 2022 07:23:01 +0000 Subject: [PATCH 002/469] Fix divider visibility issue on transient taskbar On legacy case, we hide divider bar when gesture start, but this might cause divider hidden when transient taskbar case. We should follow the timing it actually start entering overview then hide divider. Fix: 261376202 Test: manual Test: pass existing tests Change-Id: Iae1339ae0b42033075dd840fc071ba28a7d3e13a --- .../com/android/quickstep/AbsSwipeUpHandler.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 47dafabacb..eadf323656 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -323,6 +323,7 @@ public abstract class AbsSwipeUpHandler, private final boolean mIsTransientTaskbar; // May be set to false when mIsTransientTaskbar is true. private boolean mCanSlowSwipeGoHome = true; + private boolean mHasReachedOverviewThreshold = false; public AbsSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState, TaskAnimationManager taskAnimationManager, GestureState gestureState, @@ -762,6 +763,10 @@ public abstract class AbsSwipeUpHandler, private void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask, boolean animate) { if (mIsLikelyToStartNewTask != isLikelyToStartNewTask) { + if (isLikelyToStartNewTask && mIsTransientTaskbar) { + setDividerShown(false /* shown */, true /* immediate */); + } + mIsLikelyToStartNewTask = isLikelyToStartNewTask; maybeUpdateRecentsAttachedState(animate); } @@ -1656,7 +1661,9 @@ public abstract class AbsSwipeUpHandler, mRecentsAnimationController.enableInputConsumer(); // Start hiding the divider - setDividerShown(false /* shown */, true /* immediate */); + if (!mIsTransientTaskbar || mTaskbarAlreadyOpen || mIsTaskbarAllAppsOpen) { + setDividerShown(false /* shown */, true /* immediate */); + } } private void computeRecentsScrollIfInvisible() { @@ -2288,6 +2295,10 @@ public abstract class AbsSwipeUpHandler, // "Catch up" with the displacement at mTaskbarCatchUpThreshold. if (displacement < mTaskbarCatchUpThreshold) { + if (!mHasReachedOverviewThreshold) { + setDividerShown(false /* shown */, true /* immediate */); + mHasReachedOverviewThreshold = true; + } return Utilities.mapToRange(displacement, mTaskbarAppWindowThreshold, mTaskbarCatchUpThreshold, 0, mTaskbarCatchUpThreshold, ACCEL_DEACCEL); } From 8ba48c1e65015ecc35e206195675eaa749d36744 Mon Sep 17 00:00:00 2001 From: Ling Wo Date: Mon, 24 Oct 2022 20:00:53 +0000 Subject: [PATCH 003/469] Expose getSearchBox from the HomeAllApps class Doc: https://docs.google.com/document/d/1OlN-A3Z5_UjWk-QkxC4JaYWesFDNHkHTox94__rLmR0 Bug: b/255390519 Test: local testing Change-Id: I72410035bbcfd2215815dbf6c3d6bd86d692b8c6 --- .../android/launcher3/tapl/AllAppsQsb.java | 37 ++++++++ .../android/launcher3/tapl/HomeAllApps.java | 9 ++ .../com/android/launcher3/tapl/HomeQsb.java | 66 +++----------- .../tapl/com/android/launcher3/tapl/Qsb.java | 86 +++++++++++++++++++ .../launcher3/tapl/SearchResultFromQsb.java | 2 +- .../com/android/launcher3/tapl/Workspace.java | 5 +- 6 files changed, 146 insertions(+), 59 deletions(-) create mode 100644 tests/tapl/com/android/launcher3/tapl/AllAppsQsb.java create mode 100644 tests/tapl/com/android/launcher3/tapl/Qsb.java diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsQsb.java b/tests/tapl/com/android/launcher3/tapl/AllAppsQsb.java new file mode 100644 index 0000000000..0931cd46b0 --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/AllAppsQsb.java @@ -0,0 +1,37 @@ +/* + * 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.tapl; + +import androidx.test.uiautomator.UiObject2; + +/** + * Operations on AllApp screen qsb. + */ +class AllAppsQsb extends Qsb { + + private final UiObject2 mAllAppsContainer; + + AllAppsQsb(LauncherInstrumentation launcher, UiObject2 allAppsContainer) { + super(launcher); + mAllAppsContainer = allAppsContainer; + waitForQsbObject(); + } + + @Override + protected UiObject2 waitForQsbObject() { + return mLauncher.waitForObjectInContainer(mAllAppsContainer, "search_container_all_apps"); + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java index 9a4c6d4dce..ea6fd3ba29 100644 --- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java +++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java @@ -73,4 +73,13 @@ public class HomeAllApps extends AllApps { } } } + + /** + * Return the QSB UI object on the AllApps screen. + * @return the QSB UI object. + */ + @NonNull + public Qsb getQsb() { + return new AllAppsQsb(mLauncher, verifyActiveContainer()); + } } diff --git a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java index c365708d46..20d09a1e16 100644 --- a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java +++ b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java @@ -15,69 +15,23 @@ */ package com.android.launcher3.tapl; -import androidx.annotation.NonNull; -import androidx.test.uiautomator.By; -import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.UiObject2; -import androidx.test.uiautomator.Until; /** - * Operations on home screen qsb. + * Operations on Home screen qsb. */ -public class HomeQsb { +class HomeQsb extends Qsb { - private final LauncherInstrumentation mLauncher; - private static final String ASSISTANT_APP_PACKAGE = "com.google.android.googlequicksearchbox"; - private static final String ASSISTANT_ICON_RES_ID = "mic_icon"; + private final UiObject2 mHotSeat; - - HomeQsb(LauncherInstrumentation launcher) { - mLauncher = launcher; - mLauncher.waitForLauncherObject("search_container_hotseat"); + HomeQsb(LauncherInstrumentation launcher, UiObject2 hotseat) { + super(launcher); + mHotSeat = hotseat; + waitForQsbObject(); } - /** - * Launch assistant app by tapping mic icon on qsb. - */ - @NonNull - public LaunchedAppState launchAssistant() { - try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( - "want to click assistant mic icon button"); - LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) { - UiObject2 assistantIcon = mLauncher.waitForLauncherObject(ASSISTANT_ICON_RES_ID); - - LauncherInstrumentation.log("HomeQsb.launchAssistant before click " - + assistantIcon.getVisibleCenter() + " in " - + mLauncher.getVisibleBounds(assistantIcon)); - - mLauncher.clickLauncherObject(assistantIcon); - - try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) { - // assert Assistant App Launched - BySelector selector = By.pkg(ASSISTANT_APP_PACKAGE); - mLauncher.assertTrue( - "assistant app didn't start: (" + selector + ")", - mLauncher.getDevice().wait(Until.hasObject(selector), - LauncherInstrumentation.WAIT_TIME_MS) - ); - return new LaunchedAppState(mLauncher); - } - } - } - - /** - * Show search result page from tapping qsb. - */ - public SearchResultFromQsb showSearchResult() { - try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( - "want to open search result page"); - LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) { - mLauncher.clickLauncherObject( - mLauncher.waitForLauncherObject("search_container_hotseat")); - try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer( - "clicked qsb to open search result page")) { - return new SearchResultFromQsb(mLauncher); - } - } + @Override + protected UiObject2 waitForQsbObject() { + return mLauncher.waitForObjectInContainer(mHotSeat, "search_container_hotseat"); } } diff --git a/tests/tapl/com/android/launcher3/tapl/Qsb.java b/tests/tapl/com/android/launcher3/tapl/Qsb.java new file mode 100644 index 0000000000..6bc4f2109c --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/Qsb.java @@ -0,0 +1,86 @@ +/* + * 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.tapl; + +import androidx.annotation.NonNull; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.BySelector; +import androidx.test.uiautomator.UiObject2; +import androidx.test.uiautomator.Until; + +/** + * Operations on qsb from either Home screen or AllApp screen. + */ +public abstract class Qsb { + + private static final String ASSISTANT_APP_PACKAGE = "com.google.android.googlequicksearchbox"; + private static final String ASSISTANT_ICON_RES_ID = "mic_icon"; + protected final LauncherInstrumentation mLauncher; + + protected Qsb(LauncherInstrumentation launcher) { + mLauncher = launcher; + } + + // Waits for the quick search box. + protected abstract UiObject2 waitForQsbObject(); + /** + * Launch assistant app by tapping mic icon on qsb. + */ + + @NonNull + public LaunchedAppState launchAssistant() { + try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "want to click assistant mic icon button"); + LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) { + UiObject2 assistantIcon = mLauncher.waitForLauncherObject(ASSISTANT_ICON_RES_ID); + + LauncherInstrumentation.log("Qsb.launchAssistant before click " + + assistantIcon.getVisibleCenter() + " in " + + mLauncher.getVisibleBounds(assistantIcon)); + + mLauncher.clickLauncherObject(assistantIcon); + + try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) { + // assert Assistant App Launched + BySelector selector = By.pkg(ASSISTANT_APP_PACKAGE); + mLauncher.assertTrue( + "assistant app didn't start: (" + selector + ")", + mLauncher.getDevice().wait(Until.hasObject(selector), + LauncherInstrumentation.WAIT_TIME_MS) + ); + return new LaunchedAppState(mLauncher); + } + } + } + + /** + * Show search result page from tapping qsb. + */ + public SearchResultFromQsb showSearchResult() { + try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "want to open search result page"); + LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) { + mLauncher.clickLauncherObject(waitForQsbObject()); + // wait for the result rendering to complete + mLauncher.waitForIdle(); + try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer( + "clicked qsb to open search result page")) { + return new SearchResultFromQsb(mLauncher); + } + } + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java index 80e4116406..80176e993f 100644 --- a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java +++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java @@ -23,7 +23,7 @@ import androidx.test.uiautomator.UiObject2; import java.util.ArrayList; /** - * Operations on search result page opened from home screen qsb. + * Operations on search result page opened from qsb. */ public class SearchResultFromQsb { // The input resource id in the search box. diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index 7ed5788661..e5c467ac18 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -118,10 +118,11 @@ public final class Workspace extends Home { * * The qsb must already be visible when calling this method. */ - public HomeQsb getQsb() { + @NonNull + public Qsb getQsb() { try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "want to get the home qsb")) { - return new HomeQsb(mLauncher); + return new HomeQsb(mLauncher, mHotseat); } } From 8f83cc6b4561971c6d4f3bddc5ae102e2cd4222a Mon Sep 17 00:00:00 2001 From: Darrell Shi Date: Tue, 27 Dec 2022 19:42:57 +0000 Subject: [PATCH 004/469] Display back button on home settings. This change provides a back affordance on the Home settings when accessed from Hub mode settings. Bug: 263403020 Fix: 263403020 Test: Settings > Hub mode > At a glance, see back affordance Test: long press launcher > Home settings, no back affordance Change-Id: I96449d7eae4afac5ec3f61e5aa5a4f2c05b8cd51 --- src/com/android/launcher3/settings/SettingsActivity.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java index 3e2d051a0a..d3f558b5b1 100644 --- a/src/com/android/launcher3/settings/SettingsActivity.java +++ b/src/com/android/launcher3/settings/SettingsActivity.java @@ -93,7 +93,8 @@ public class SettingsActivity extends FragmentActivity WindowCompat.setDecorFitsSystemWindows(getWindow(), false); Intent intent = getIntent(); - if (intent.hasExtra(EXTRA_FRAGMENT) || intent.hasExtra(EXTRA_FRAGMENT_ARGS)) { + if (intent.hasExtra(EXTRA_FRAGMENT) || intent.hasExtra(EXTRA_FRAGMENT_ARGS) + || intent.hasExtra(EXTRA_FRAGMENT_ARG_KEY)) { getActionBar().setDisplayHomeAsUpEnabled(true); } From 81f7d0bbf58826127c3fbc2d36f5c54623526781 Mon Sep 17 00:00:00 2001 From: Shan Huang Date: Thu, 29 Dec 2022 03:15:03 +0000 Subject: [PATCH 005/469] DO NOT MERGE Rename BackEvent to BackMotionEvent in LauncherBackAnimationController. This cherry-picks ag/20484685 to QPR to mitigate future merge conflicts. Bug: 238475284 Test: m -j Change-Id: Ie13c738d0d4a10f4265fbf116ff7b4206d3bfc62 --- .../android/quickstep/LauncherBackAnimationController.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java index 27417516a4..3edbbdf983 100644 --- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java +++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java @@ -36,6 +36,7 @@ import android.view.SurfaceControl; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.window.BackEvent; +import android.window.BackMotionEvent; import android.window.BackProgressAnimator; import android.window.IOnBackInvokedCallback; @@ -134,14 +135,14 @@ public class LauncherBackAnimationController { } @Override - public void onBackProgressed(BackEvent backEvent) { + public void onBackProgressed(BackMotionEvent backEvent) { handler.post(() -> { mProgressAnimator.onBackProgressed(backEvent); }); } @Override - public void onBackStarted(BackEvent backEvent) { + public void onBackStarted(BackMotionEvent backEvent) { handler.post(() -> { startBack(backEvent); mProgressAnimator.onBackStarted(backEvent, event -> { @@ -185,7 +186,7 @@ public class LauncherBackAnimationController { mBackCallback = null; } - private void startBack(BackEvent backEvent) { + private void startBack(BackMotionEvent backEvent) { mBackInProgress = true; RemoteAnimationTarget appTarget = backEvent.getDepartingAnimationTarget(); From 377eb4098521a9a41827c47df1ba633bf9bb9748 Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Mon, 12 Dec 2022 06:23:34 -0800 Subject: [PATCH 006/469] Revert^2 "Cancel gestures on launcher destroy" f0de6cc2cad5e2224bccf60cab31b26468ba0231 Bug: 261816852 Fixes: 244593270 Fixes: 257976590 Fixes: 261504234 Test: FallbackRecentsTest#goToOverviewFromApp and FallbackRecentsTest#goToOverviewFromHome Change-Id: If49a08fd62b99c9bbc007900b613b83747916f5f --- .../android/quickstep/AbsSwipeUpHandler.java | 12 +++++++ .../quickstep/BaseActivityInterface.java | 8 +++++ .../quickstep/FallbackActivityInterface.java | 5 +++ .../android/quickstep/MultiStateCallback.java | 35 +++++++++++++++++-- .../util/ActiveGestureErrorDetector.java | 9 ++++- .../quickstep/FallbackRecentsTest.java | 2 -- 6 files changed, 66 insertions(+), 5 deletions(-) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 3d8ffc418a..d3d9a6ee55 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -50,6 +50,7 @@ import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHE import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.CANCEL_RECENTS_ANIMATION; import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.EXPECTING_TASK_APPEARED; +import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.LAUNCHER_DESTROYED; import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_SETTLED_ON_END_TARGET; import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC; import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD; @@ -181,6 +182,7 @@ public abstract class AbsSwipeUpHandler, if (mActivity != activity) { return; } + handleActivityDestroyed(); mRecentsView = null; mActivity = null; } @@ -451,6 +453,7 @@ public abstract class AbsSwipeUpHandler, if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) { return false; } + mStateCallback.resumeCallbacks(); T createdActivity = mActivityInterface.getCreatedActivity(); if (createdActivity != null) { @@ -520,6 +523,15 @@ public abstract class AbsSwipeUpHandler, return true; } + private void handleActivityDestroyed() { + ActiveGestureLog.INSTANCE.addLog("Launcher activity destroyed", LAUNCHER_DESTROYED); + if (mActivityInterface.shouldCancelGestureOnDestroy()) { + onGestureCancelled(); + } else { + mStateCallback.pauseCallbacks(); + } + } + /** * Return true if the window should be translated horizontally if the recents view scrolls */ diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java index 274b686ea4..22eaa972c3 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -123,6 +123,14 @@ public abstract class BaseActivityInterface callback); + /** + * Returns {@code true} iff an ongoing navigational gesture should be cancelled on activity + * destroy. Otherwise, the MultiStateCallbacks will be paused until the activity is recreated. + */ + public boolean shouldCancelGestureOnDestroy() { + return true; + } + public abstract ActivityInitListener createActivityInitListener( Predicate onInitListener); diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java index ae9fb0b385..4cb4665d54 100644 --- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java +++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java @@ -87,6 +87,11 @@ public final class FallbackActivityInterface extends return factory; } + @Override + public boolean shouldCancelGestureOnDestroy() { + return false; + } + @Override public ActivityInitListener createActivityInitListener( Predicate onInitListener) { diff --git a/quickstep/src/com/android/quickstep/MultiStateCallback.java b/quickstep/src/com/android/quickstep/MultiStateCallback.java index a68bea2cc0..6d767ed92e 100644 --- a/quickstep/src/com/android/quickstep/MultiStateCallback.java +++ b/quickstep/src/com/android/quickstep/MultiStateCallback.java @@ -31,6 +31,7 @@ import com.android.quickstep.util.ActiveGestureLog; import java.util.ArrayList; import java.util.LinkedList; +import java.util.List; import java.util.StringJoiner; import java.util.function.Consumer; @@ -52,6 +53,9 @@ public class MultiStateCallback { private int mState = 0; + private boolean mCallbacksPaused = false; + private final List mPendingCallbacks = new ArrayList<>(); + public MultiStateCallback(String[] stateNames) { this(stateNames, stateFlag -> null); } @@ -78,6 +82,24 @@ public class MultiStateCallback { } } + /** Pauses callbacks. */ + public void pauseCallbacks() { + mCallbacksPaused = true; + } + + /** Immediately queues any callbacks that were pending paused. */ + public void resumeCallbacks() { + if (!mCallbacksPaused) { + return; + } + mCallbacksPaused = false; + List queuedCallbacks = new ArrayList<>(mPendingCallbacks); + mPendingCallbacks.clear(); + for (Runnable runnable : queuedCallbacks) { + runnable.run(); + } + } + /** * Adds the provided state flags to the global state and executes any callbacks as a result. */ @@ -99,7 +121,12 @@ public class MultiStateCallback { if ((mState & state) == state) { LinkedList callbacks = mCallbacks.valueAt(i); while (!callbacks.isEmpty()) { - callbacks.pollFirst().run(); + Runnable cb = callbacks.pollFirst(); + if (mCallbacksPaused) { + mPendingCallbacks.add(cb); + } else { + cb.run(); + } } } } @@ -151,7 +178,11 @@ public class MultiStateCallback { if (wasOn != isOn) { ArrayList> listeners = mStateChangeListeners.valueAt(i); for (Consumer listener : listeners) { - listener.accept(isOn); + if (mCallbacksPaused) { + mPendingCallbacks.add(() -> listener.accept(isOn)); + } else { + listener.accept(isOn); + } } } } diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java index 60065fb16c..0fdd8b5e36 100644 --- a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java +++ b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java @@ -37,7 +37,7 @@ public class ActiveGestureErrorDetector { ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION, FINISH_RECENTS_ANIMATION, CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK, CANCEL_CURRENT_ANIMATION, CLEANUP_SCREENSHOT, SCROLLER_ANIMATION_ABORTED, TASK_APPEARED, EXPECTING_TASK_APPEARED, - FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER, + FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER, LAUNCHER_DESTROYED, /** * These GestureEvents are specifically associated to state flags that get set in @@ -162,6 +162,13 @@ public class ActiveGestureErrorDetector { + "before/without setting end target to new task", writer); break; + case LAUNCHER_DESTROYED: + errorDetected |= printErrorIfTrue( + true, + prefix, + /* errorMessage= */ "Launcher destroyed mid-gesture", + writer); + break; case STATE_GESTURE_COMPLETED: errorDetected |= printErrorIfTrue( !encounteredEvents.contains(GestureEvent.MOTION_UP), diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java index 47bef7b5b2..a4dbf6ade6 100644 --- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java +++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java @@ -168,7 +168,6 @@ public class FallbackRecentsTest { // b/143488140 //@NavigationModeSwitch - @Ignore @Test public void goToOverviewFromHome() { mDevice.pressHome(); @@ -216,7 +215,6 @@ public class FallbackRecentsTest { // b/143488140 //@NavigationModeSwitch - @Ignore @Test public void testOverview() { startAppFast(getAppPackageName()); From 50f66511d932e3555bb6de288987d657bd81bd7a Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Wed, 14 Dec 2022 02:53:38 -0800 Subject: [PATCH 007/469] Fix IllegalStateException caused by trying to snap to destination before initalizing page scrolls. Bug: 262256539 Test: swiped home several times Change-Id: I1d4b515f917e89c2ee0112a408499734222c6852 --- src/com/android/launcher3/PagedView.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index eb68adb3e8..80b1a8c019 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -1404,15 +1404,17 @@ public abstract class PagedView extends ViewGrou (isFling && !isVelocityLeft)) && mCurrentPage > 0) { finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - getPanelCount(); - snapToPageWithVelocity(finalPage, velocity); + runOnPageScrollsInitialized( + () -> snapToPageWithVelocity(finalPage, velocity)); } else if (((isSignificantMove && isDeltaLeft && !isFling) || (isFling && isVelocityLeft)) && mCurrentPage < getChildCount() - 1) { finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + getPanelCount(); - snapToPageWithVelocity(finalPage, velocity); + runOnPageScrollsInitialized( + () -> snapToPageWithVelocity(finalPage, velocity)); } else { - snapToDestination(); + runOnPageScrollsInitialized(this::snapToDestination); } } else { if (!mScroller.isFinished()) { @@ -1435,7 +1437,7 @@ public abstract class PagedView extends ViewGrou int finalPos = mScroller.getFinalX(); mNextPage = getDestinationPage(finalPos); - onNotSnappingToPageInFreeScroll(); + runOnPageScrollsInitialized(this::onNotSnappingToPageInFreeScroll); } invalidate(); } @@ -1449,7 +1451,7 @@ public abstract class PagedView extends ViewGrou case MotionEvent.ACTION_CANCEL: if (mIsBeingDragged) { - snapToDestination(); + runOnPageScrollsInitialized(this::snapToDestination); } mEdgeGlowLeft.onRelease(); mEdgeGlowRight.onRelease(); From 7764627216d41af971ff481bb866bc49c6954257 Mon Sep 17 00:00:00 2001 From: Jagrut Desai Date: Wed, 4 Jan 2023 13:46:11 -0500 Subject: [PATCH 008/469] Moving Accessibility Annoucement to Task Closed to onEnd animation callback. Test: Manual Fix: 220198279 Change-Id: I3d9d522cd81b4873fb4b03671384f23b7bb6777f --- quickstep/src/com/android/quickstep/views/RecentsView.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 68628746a5..41a6673dc9 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -3178,8 +3178,6 @@ public abstract class RecentsView Date: Tue, 3 Jan 2023 16:50:11 -0800 Subject: [PATCH 009/469] Fixes to support transient taskbar in desktop mode Track gesture progress in desktop visibility controller. We need to allow launcher to resume when gesture is in progress and freeform tasks are visible. Update overview state handling in desktop visibility controller. When overview is enabled, launcher should be made visible, regardless of freeform state. When exiting overview state, check freeform state to see what should be shown and enabled. Bug: 263264985 Test: manual, enable transient taskbar, move app to desktop, invoke transient taskbar Test: manual, enable transient taskarb, enable desktop mode, invoke transient taskbar Test: manual, disable transient taskbar, move app to desktop, swipe up to overview Test: manual, disable transient taskbar, enable desktop mode, swipe up to overview Change-Id: I63000441d9cf72769e6efb9d247ab4112c01839d --- .../DesktopVisibilityController.java | 93 ++++++++++++++----- .../uioverrides/QuickstepLauncher.java | 3 +- .../quickstep/views/LauncherRecentsView.java | 24 +++++ 3 files changed, 94 insertions(+), 26 deletions(-) diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java index bbc0627517..ae121e2ef4 100644 --- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java +++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java @@ -33,6 +33,7 @@ public class DesktopVisibilityController { private boolean mFreeformTasksVisible; private boolean mInOverviewState; + private boolean mGestureInProgress; public DesktopVisibilityController(Launcher launcher) { mLauncher = launcher; @@ -57,9 +58,24 @@ public class DesktopVisibilityController { * Sets whether freeform windows are visible and updates launcher visibility based on that. */ public void setFreeformTasksVisible(boolean freeformTasksVisible) { + if (!isDesktopModeSupported()) { + return; + } if (freeformTasksVisible != mFreeformTasksVisible) { mFreeformTasksVisible = freeformTasksVisible; - updateLauncherVisibility(); + if (mFreeformTasksVisible) { + setLauncherViewsVisibility(View.INVISIBLE); + if (!mInOverviewState) { + // When freeform is visible & we're not in overview, we want launcher to appear + // paused, this ensures that taskbar displays. + markLauncherPaused(); + } + } else { + setLauncherViewsVisibility(View.VISIBLE); + // If freeform isn't visible ensure that launcher appears resumed to behave + // normally. + markLauncherResumed(); + } } } @@ -67,40 +83,67 @@ public class DesktopVisibilityController { * Sets whether the overview is visible and updates launcher visibility based on that. */ public void setOverviewStateEnabled(boolean overviewStateEnabled) { + if (!isDesktopModeSupported()) { + return; + } if (overviewStateEnabled != mInOverviewState) { mInOverviewState = overviewStateEnabled; - updateLauncherVisibility(); + if (mInOverviewState) { + setLauncherViewsVisibility(View.VISIBLE); + markLauncherResumed(); + } else if (mFreeformTasksVisible) { + setLauncherViewsVisibility(View.INVISIBLE); + markLauncherPaused(); + } } } /** - * Updates launcher visibility and state to look like it is paused or resumed depending on - * whether freeform windows are showing in desktop mode. + * Whether recents gesture is currently in progress. */ - private void updateLauncherVisibility() { - StatefulActivity activity = - QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity(); - View workspaceView = mLauncher.getWorkspace(); - if (activity == null || workspaceView == null || !isDesktopModeSupported()) { + public boolean isGestureInProgress() { + return mGestureInProgress; + } + + /** + * Sets whether recents gesture is in progress. + */ + public void setGestureInProgress(boolean gestureInProgress) { + if (!isDesktopModeSupported()) { return; } + if (gestureInProgress != mGestureInProgress) { + mGestureInProgress = gestureInProgress; + } + } - if (mFreeformTasksVisible) { - workspaceView.setVisibility(View.INVISIBLE); - if (!mInOverviewState) { - // When freeform is visible & we're not in overview, we want launcher to appear - // paused, this ensures that taskbar displays. - activity.setPaused(); - } - } else { - workspaceView.setVisibility(View.VISIBLE); - // If freeform isn't visible ensure that launcher appears resumed to behave normally. - // Check activity state before calling setResumed(). Launcher may have been actually - // paused (eg fullscreen task moved to front). - // In this case we should not mark the activity as resumed. - if (activity.isResumed()) { - activity.setResumed(); - } + private void setLauncherViewsVisibility(int visibility) { + View workspaceView = mLauncher.getWorkspace(); + if (workspaceView != null) { + workspaceView.setVisibility(visibility); + } + View dragLayer = mLauncher.getDragLayer(); + if (dragLayer != null) { + dragLayer.setVisibility(visibility); + } + } + + private void markLauncherPaused() { + StatefulActivity activity = + QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity(); + if (activity != null) { + activity.setPaused(); + } + } + + private void markLauncherResumed() { + StatefulActivity activity = + QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity(); + // Check activity state before calling setResumed(). Launcher may have been actually + // paused (eg fullscreen task moved to front). + // In this case we should not mark the activity as resumed. + if (activity != null && activity.isResumed()) { + activity.setResumed(); } } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 9be569e895..28c8980668 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -668,7 +668,8 @@ public class QuickstepLauncher extends Launcher { public void setResumed() { if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) { DesktopVisibilityController controller = mDesktopVisibilityController; - if (controller != null && controller.areFreeformTasksVisible()) { + if (controller != null && controller.areFreeformTasksVisible() + && !controller.isGestureInProgress()) { // Return early to skip setting activity to appear as resumed // TODO(b/255649902): shouldn't be needed when we have a separate launcher state // for desktop that we can use to control other parts of launcher diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java index 6c27587058..cf033a6287 100644 --- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java @@ -36,12 +36,15 @@ import com.android.launcher3.LauncherState; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.popup.QuickstepSystemShortcut; import com.android.launcher3.statehandlers.DepthController; +import com.android.launcher3.statehandlers.DesktopVisibilityController; import com.android.launcher3.statemanager.StateManager.StateListener; import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.util.PendingSplitSelectInfo; import com.android.launcher3.util.SplitConfigurationOptions; import com.android.quickstep.LauncherActivityInterface; +import com.android.quickstep.RotationTouchHelper; import com.android.quickstep.util.SplitSelectStateController; +import com.android.systemui.shared.recents.model.Task; /** * {@link RecentsView} used in Launcher activity @@ -205,4 +208,25 @@ public class LauncherRecentsView extends RecentsView Date: Fri, 16 Dec 2022 10:12:17 +0000 Subject: [PATCH 010/469] Make LauncherAnimationRunner compatible with RemoteAnimationRunner. Nothing changes in its current usage and behavior, but instead of a RemoteAnimationFactory we can now also pass a RemoteAnimationRunner. Bug: 250588519 Test: manual Change-Id: I51f5a95360401d5f17104519ef91a81aef60923f --- .../launcher3/LauncherAnimationRunner.java | 33 ++++++++++++--- .../launcher3/QuickstepTransitionManager.java | 4 +- .../android/quickstep/RecentsActivity.java | 40 +++++++++---------- 3 files changed, 48 insertions(+), 29 deletions(-) diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java index 95a94ec149..9f9f2c8654 100644 --- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java +++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java @@ -28,12 +28,15 @@ import android.annotation.TargetApi; import android.content.Context; import android.os.Build; import android.os.Handler; +import android.os.RemoteException; +import android.view.IRemoteAnimationFinishedCallback; import android.view.RemoteAnimationTarget; import androidx.annotation.BinderThread; import androidx.annotation.Nullable; import androidx.annotation.UiThread; +import com.android.systemui.animation.RemoteAnimationDelegate; import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; import java.lang.ref.WeakReference; @@ -89,7 +92,7 @@ public class LauncherAnimationRunner extends RemoteAnimationRunnerCompat { Runnable r = () -> { finishExistingAnimation(); mAnimationResult = new AnimationResult(() -> mAnimationResult = null, runnable); - getFactory().onCreateAnimation(transit, appTargets, wallpaperTargets, nonAppTargets, + getFactory().onAnimationStart(transit, appTargets, wallpaperTargets, nonAppTargets, mAnimationResult); }; if (mStartAtFrontOfQueue) { @@ -124,7 +127,11 @@ public class LauncherAnimationRunner extends RemoteAnimationRunnerCompat { }); } - public static final class AnimationResult { + /** + * Used by RemoteAnimationFactory implementations to run the actual animation and its lifecycle + * callbacks. + */ + public static final class AnimationResult extends IRemoteAnimationFinishedCallback.Stub { private final Runnable mSyncFinishRunnable; private final Runnable mASyncFinishRunnable; @@ -199,25 +206,41 @@ public class LauncherAnimationRunner extends RemoteAnimationRunnerCompat { } } } + + /** + * When used as a simple IRemoteAnimationFinishedCallback, this method is used to run the + * animation finished runnable. + */ + @Override + public void onAnimationFinished() throws RemoteException { + mASyncFinishRunnable.run(); + } } /** * Used with LauncherAnimationRunner as an interface for the runner to call back to the * implementation. */ - @FunctionalInterface - public interface RemoteAnimationFactory { + public interface RemoteAnimationFactory extends RemoteAnimationDelegate { /** * Called on the UI thread when the animation targets are received. The implementation must * call {@link AnimationResult#setAnimation} with the target animation to be run. */ - void onCreateAnimation(int transit, + @Override + @UiThread + void onAnimationStart(int transit, RemoteAnimationTarget[] appTargets, RemoteAnimationTarget[] wallpaperTargets, RemoteAnimationTarget[] nonAppTargets, LauncherAnimationRunner.AnimationResult result); + @Override + @UiThread + default void onAnimationCancelled(boolean isKeyguardOccluded) { + onAnimationCancelled(); + } + /** * Called when the animation is cancelled. This can happen with or without * the create being called. diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 2aa0af4113..668567eae4 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -1665,7 +1665,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } @Override - public void onCreateAnimation(int transit, + public void onAnimationStart(int transit, RemoteAnimationTarget[] appTargets, RemoteAnimationTarget[] wallpaperTargets, RemoteAnimationTarget[] nonAppTargets, @@ -1707,7 +1707,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } @Override - public void onCreateAnimation(int transit, + public void onAnimationStart(int transit, RemoteAnimationTarget[] appTargets, RemoteAnimationTarget[] wallpaperTargets, RemoteAnimationTarget[] nonAppTargets, diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java index dc405ff8ba..6f86bf5de6 100644 --- a/quickstep/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/src/com/android/quickstep/RecentsActivity.java @@ -240,7 +240,7 @@ public final class RecentsActivity extends StatefulActivity { mActivityLaunchAnimationRunner = new RemoteAnimationFactory() { @Override - public void onCreateAnimation(int transit, RemoteAnimationTarget[] appTargets, + public void onAnimationStart(int transit, RemoteAnimationTarget[] appTargets, RemoteAnimationTarget[] wallpaperTargets, RemoteAnimationTarget[] nonAppTargets, AnimationResult result) { mHandler.removeCallbacks(mAnimationStartTimeoutRunnable); @@ -407,28 +407,24 @@ public final class RecentsActivity extends StatefulActivity { } private final RemoteAnimationFactory mAnimationToHomeFactory = - new RemoteAnimationFactory() { - @Override - public void onCreateAnimation(int transit, RemoteAnimationTarget[] appTargets, - RemoteAnimationTarget[] wallpaperTargets, - RemoteAnimationTarget[] nonAppTargets, AnimationResult result) { - AnimatorPlaybackController controller = getStateManager() - .createAnimationToNewWorkspace(RecentsState.BG_LAUNCHER, HOME_APPEAR_DURATION); - controller.dispatchOnStart(); + (transit, appTargets, wallpaperTargets, nonAppTargets, result) -> { + AnimatorPlaybackController controller = + getStateManager().createAnimationToNewWorkspace( + RecentsState.BG_LAUNCHER, HOME_APPEAR_DURATION); + controller.dispatchOnStart(); - RemoteAnimationTargets targets = new RemoteAnimationTargets( - appTargets, wallpaperTargets, nonAppTargets, MODE_OPENING); - for (RemoteAnimationTarget app : targets.apps) { - new Transaction().setAlpha(app.leash, 1).apply(); - } - AnimatorSet anim = new AnimatorSet(); - anim.play(controller.getAnimationPlayer()); - anim.setDuration(HOME_APPEAR_DURATION); - result.setAnimation(anim, RecentsActivity.this, - () -> getStateManager().goToState(RecentsState.HOME, false), - true /* skipFirstFrame */); - } - }; + RemoteAnimationTargets targets = new RemoteAnimationTargets( + appTargets, wallpaperTargets, nonAppTargets, MODE_OPENING); + for (RemoteAnimationTarget app : targets.apps) { + new Transaction().setAlpha(app.leash, 1).apply(); + } + AnimatorSet anim = new AnimatorSet(); + anim.play(controller.getAnimationPlayer()); + anim.setDuration(HOME_APPEAR_DURATION); + result.setAnimation(anim, RecentsActivity.this, + () -> getStateManager().goToState(RecentsState.HOME, false), + true /* skipFirstFrame */); + }; @Override protected void collectStateHandlers(List out) { From f34f35fddbf098bf79dd036fb79669e812970ad5 Mon Sep 17 00:00:00 2001 From: Sebastian Franco Date: Thu, 8 Dec 2022 11:08:00 -0800 Subject: [PATCH 011/469] Printing the workspace items to improve flake investigation. Recording the screen to investigates flakes on ReorderWidgets is really useful but it requires changing the code for that and storing the video and most of the time the useful information is just the end state so by logging the end state makes resolving flakes easier Fix: 261877803 Test: atest ReorderWidgets Change-Id: If5d9745ebb57826a55b5df9d3599eed56c48298f --- src/com/android/launcher3/Workspace.java | 5 +- .../launcher3/celllayout/CellLayoutBoard.java | 81 +++++++++++++++++++ .../launcher3/celllayout/ReorderWidgets.java | 39 +++++++++ 3 files changed, 124 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 483309d1cf..2b9c1359b7 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -62,6 +62,7 @@ import android.widget.FrameLayout; import android.widget.Toast; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import com.android.launcher3.accessibility.AccessibleDragListenerAdapter; import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper; @@ -161,7 +162,9 @@ public class Workspace extends PagedView protected ShortcutAndWidgetContainer mDragSourceInternal; - @Thunk final IntSparseArrayMap mWorkspaceScreens = new IntSparseArrayMap<>(); + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + @Thunk public final IntSparseArrayMap mWorkspaceScreens = new IntSparseArrayMap<>(); + @Thunk final IntArray mScreenOrder = new IntArray(); @Thunk boolean mDeferRemoveExtraEmptyScreen = false; diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java index a32ce3c558..469d79f74e 100644 --- a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java +++ b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Set; +import java.util.stream.Collectors; public class CellLayoutBoard { @@ -140,6 +141,73 @@ public class CellLayoutBoard { return mWidgetsMap.get(c); } + private void removeWidgetFromBoard(WidgetRect widget) { + for (int xi = widget.mBounds.left; xi < widget.mBounds.right; xi++) { + for (int yi = widget.mBounds.top; yi < widget.mBounds.bottom; yi++) { + mWidget[xi][yi] = '-'; + } + } + } + + private void removeOverlappingItems(Rect rect) { + // Remove overlapping widgets and remove them from the board + mWidgetsRects = mWidgetsRects.stream().filter(widget -> { + if (rect.intersect(widget.mBounds)) { + removeWidgetFromBoard(widget); + return false; + } + return true; + }).collect(Collectors.toList()); + // Remove overlapping icons and remove them from the board + mIconPoints = mIconPoints.stream().filter(iconPoint -> { + int x = iconPoint.coord.x; + int y = iconPoint.coord.y; + if (rect.contains(x, y)) { + mWidget[x][y] = '-'; + return false; + } + return true; + }).collect(Collectors.toList()); + } + + private void removeOverlappingItems(Point p) { + // Remove overlapping widgets and remove them from the board + mWidgetsRects = mWidgetsRects.stream().filter(widget -> { + if (widget.mBounds.contains(p.x, p.y)) { + removeWidgetFromBoard(widget); + return false; + } + return true; + }).collect(Collectors.toList()); + // Remove overlapping icons and remove them from the board + mIconPoints = mIconPoints.stream().filter(iconPoint -> { + int x = iconPoint.coord.x; + int y = iconPoint.coord.y; + if (p.x == x && p.y == y) { + mWidget[x][y] = '-'; + return false; + } + return true; + }).collect(Collectors.toList()); + } + + public void addWidget(int x, int y, int spanX, int spanY, char type) { + Rect rect = new Rect(x, y + spanY - 1, x + spanX - 1, y); + removeOverlappingItems(rect); + WidgetRect widgetRect = new WidgetRect(type, rect); + mWidgetsRects.add(widgetRect); + for (int xi = rect.left; xi < rect.right + 1; xi++) { + for (int yi = rect.bottom; yi < rect.top + 1; yi++) { + mWidget[xi][yi] = type; + } + } + } + + public void addIcon(int x, int y) { + removeOverlappingItems(new Point(x, y)); + mWidget[x][y] = 'i'; + } + public static WidgetRect getWidgetRect(int x, int y, Set used, char[][] board) { char type = board[x][y]; Queue search = new ArrayDeque(); @@ -227,4 +295,17 @@ public class CellLayoutBoard { board.mIconPoints = getIconPoints(board.mWidget); return board; } + + public String toString(int maxX, int maxY) { + StringBuilder s = new StringBuilder(); + maxX = Math.min(maxX, mWidget.length); + maxY = Math.min(maxY, mWidget[0].length); + for (int y = 0; y < maxY; y++) { + for (int x = 0; x < maxX; x++) { + s.append(mWidget[x][y]); + } + s.append('\n'); + } + return s.toString(); + } } diff --git a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java index 9da7e0f39b..843f0112ba 100644 --- a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java +++ b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java @@ -26,6 +26,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.launcher3.CellLayout; +import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.celllayout.testcases.FullReorderCase; import com.android.launcher3.celllayout.testcases.MoveOutReorderCase; import com.android.launcher3.celllayout.testcases.PushReorderCase; @@ -46,6 +47,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; import java.util.Map; import java.util.concurrent.ExecutionException; @@ -108,6 +110,42 @@ public class ReorderWidgets extends AbstractLauncherUiTest { return match; } + private void printCurrentWorkspace() { + InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mTargetContext); + ArrayList boards = workspaceToBoards(); + for (int i = 0; i < boards.size(); i++) { + Log.d(TAG, "Screen number " + i); + Log.d(TAG, ".\n" + boards.get(i).toString(idp.numColumns, idp.numRows)); + } + } + + private ArrayList workspaceToBoards() { + return getFromLauncher(l -> { + ArrayList boards = new ArrayList<>(); + int widgetCount = 0; + for (CellLayout cellLayout : l.getWorkspace().mWorkspaceScreens) { + CellLayoutBoard board = new CellLayoutBoard(); + int count = cellLayout.getShortcutsAndWidgets().getChildCount(); + for (int i = 0; i < count; i++) { + View callView = cellLayout.getShortcutsAndWidgets().getChildAt(i); + CellLayoutLayoutParams params = + (CellLayoutLayoutParams) callView.getLayoutParams(); + // is icon + if (callView instanceof DoubleShadowBubbleTextView) { + board.addIcon(params.cellX, params.cellY); + } else { + // is widget + board.addWidget(params.cellX, params.cellY, params.cellHSpan, + params.cellVSpan, (char) ('A' + widgetCount)); + widgetCount++; + } + } + boards.add(board); + } + return boards; + }); + } + private void runTestCase(ReorderTestCase testCase) throws ExecutionException, InterruptedException { Point mainWidgetCellPos = testCase.mStart.getMain(); @@ -127,6 +165,7 @@ public class ReorderWidgets extends AbstractLauncherUiTest { for (CellLayoutBoard board : testCase.mEnd) { isValid |= validateBoard(board); } + printCurrentWorkspace(); assertTrue("Non of the valid boards match with the current state", isValid); } From 04658bcd42913aefacf8fa81d4bdf05db89cca15 Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Mon, 9 Jan 2023 12:43:16 +0000 Subject: [PATCH 012/469] Add debug logs for hotseat disapperaed bug Bug: 260135164 Test: manual Change-Id: Ic911ba0353b742feb753024543ca19930f11b47d --- .../launcher3/taskbar/TaskbarLauncherStateController.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index 4ad3858de1..7a75661329 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -160,6 +160,7 @@ import java.util.StringJoiner; mIconAlignment.finishAnimation(); + Log.d("b/260135164", "onDestroy - updateIconAlphaForHome(1)"); mLauncher.getHotseat().setIconsAlpha(1f); mLauncher.getStateManager().removeStateListener(mStateListener); @@ -404,6 +405,8 @@ import java.util.StringJoiner; public void onAnimationEnd(Animator animation) { if (isInStashedState && committed) { // Reset hotseat alpha to default + Log.d("b/260135164", + "playStateTransitionAnim#onAnimationEnd - setIconsAlpha(1)"); mLauncher.getHotseat().setIconsAlpha(1); } } @@ -452,6 +455,9 @@ import java.util.StringJoiner; * Hide Launcher Hotseat icons when Taskbar icons have opacity. Both icon sets * should not be visible at the same time. */ + Log.d("b/260135164", + "updateIconAlphaForHome - setIconsAlpha(" + (hotseatVisible ? 1 : 0) + + "), isTaskbarPresent: " + mLauncher.getDeviceProfile().isTaskbarPresent); mLauncher.getHotseat().setIconsAlpha(hotseatVisible ? 1 : 0); mLauncher.getHotseat().setQsbAlpha( mLauncher.getDeviceProfile().isQsbInline && !hotseatVisible ? 0 : 1); From 1cf1e3b025a35e4317e085d8ce7f40f0fff3f404 Mon Sep 17 00:00:00 2001 From: Sebastian Franco Date: Mon, 9 Jan 2023 14:18:28 -0600 Subject: [PATCH 013/469] Removing screen recording for testShortcutIconWithTheme Bug: 260722220 Test: compiles Change-Id: I7f5ac09bc7e9deb9af3e90dbd99826bb8c4db11f --- .../src/com/android/launcher3/ui/workspace/ThemeIconsTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java index 9dae00c29c..7ba0b53b22 100644 --- a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java +++ b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java @@ -36,7 +36,6 @@ import com.android.launcher3.tapl.HomeAppIconMenuItem; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.TaplTestsLauncher3; import com.android.launcher3.util.Executors; -import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord; import org.junit.Test; @@ -111,7 +110,6 @@ public class ThemeIconsTest extends AbstractLauncherUiTest { } @Test - @ScreenRecord // b/260722220 public void testShortcutIconWithTheme() throws Exception { setThemeEnabled(true); TaplTestsLauncher3.initialize(this); From 03ac377a34d58bc270c6a0021d693d427dcc8a85 Mon Sep 17 00:00:00 2001 From: Thales Lima Date: Fri, 6 Jan 2023 15:16:41 +0000 Subject: [PATCH 014/469] Check kotlin format before uploading This uses ktfmt on the PREUPLOAD hook to check file formatting. Bug: 264851618 Test: manual Change-Id: I8bdeabd9c1ed571a464707e6912419ad3190a169 Merged-In: I8bdeabd9c1ed571a464707e6912419ad3190a169 --- PREUPLOAD.cfg | 2 + .../taskbar/TaskbarBackgroundRenderer.kt | 30 +- .../taskbar/TaskbarInsetsController.kt | 80 ++-- .../VoiceInteractionWindowController.kt | 68 +-- .../navbutton/AbstractNavButtonLayoutter.kt | 21 +- .../taskbar/navbutton/KidsNavLayoutter.kt | 53 +-- .../navbutton/NavButtonLayoutFactory.kt | 84 ++-- .../navbutton/PhoneLandscapeNavLayoutter.kt | 24 +- .../navbutton/PhonePortraitNavLayoutter.kt | 25 +- .../taskbar/navbutton/SetupNavLayoutter.kt | 15 +- .../taskbar/navbutton/TaskbarNavLayoutter.kt | 19 +- .../com/android/quickstep/util/LogUtils.kt | 21 +- .../quickstep/views/TaskMenuViewWithArrow.kt | 138 +++--- .../navbutton/NavButtonLayoutFactoryTest.kt | 118 +++-- .../quickstep/FullscreenDrawParamsTest.kt | 173 +++++--- .../quickstep/HotseatWidthCalculationTest.kt | 20 +- src/com/android/launcher3/LauncherPrefs.kt | 11 +- .../android/launcher3/util/DimensionUtils.kt | 13 +- .../launcher3/DeviceProfileBaseTest.kt | 379 ++++++++-------- .../model/AbstractWorkspaceModelTest.kt | 33 +- .../model/AddWorkspaceItemsTaskTest.kt | 107 ++--- .../model/GridSizeMigrationUtilTest.kt | 404 ++++++++++-------- .../model/WorkspaceItemSpaceFinderTest.kt | 28 +- .../HotseatWidthCalculationTest.kt | 20 +- .../launcher3/util/KotlinMockitoHelpers.kt | 33 +- .../util/MultiPropertyFactoryTest.kt | 20 +- .../android/launcher3/util/TouchUtilTest.kt | 2 +- 27 files changed, 1038 insertions(+), 903 deletions(-) diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 912395970f..a77791f78b 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -1,2 +1,4 @@ [Hook Scripts] checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --config_xml tools/checkstyle.xml --sha ${PREUPLOAD_COMMIT} + +ktfmt_hook = ${REPO_ROOT}/external/ktfmt/ktfmt.py --check ${PREUPLOAD_FILES} \ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt index ff7e8e9af2..cf3af08819 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt @@ -16,20 +16,17 @@ package com.android.launcher3.taskbar -import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound -import com.android.launcher3.Utilities.mapToRange - import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.graphics.Path import com.android.launcher3.R +import com.android.launcher3.Utilities.mapToRange import com.android.launcher3.anim.Interpolators +import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound import com.android.launcher3.util.DisplayController -/** - * Helps draw the taskbar background, made up of a rectangle plus two inverted rounded corners. - */ +/** Helps draw the taskbar background, made up of a rectangle plus two inverted rounded corners. */ class TaskbarBackgroundRenderer(context: TaskbarActivityContext) { val paint: Paint = Paint() @@ -39,7 +36,7 @@ class TaskbarBackgroundRenderer(context: TaskbarActivityContext) { private var maxBackgroundHeight = context.deviceProfile.taskbarSize.toFloat() private val transientBackgroundBounds = context.transientTaskbarBounds - private val isTransientTaskbar = DisplayController.isTransientTaskbar(context); + private val isTransientTaskbar = DisplayController.isTransientTaskbar(context) private var shadowBlur = 0f private var keyShadowDistance = 0f @@ -98,9 +95,7 @@ class TaskbarBackgroundRenderer(context: TaskbarActivityContext) { invertedRightCornerPath.op(square, circle, Path.Op.DIFFERENCE) } - /** - * Draws the background with the given paint and height, on the provided canvas. - */ + /** Draws the background with the given paint and height, on the provided canvas. */ fun draw(canvas: Canvas) { canvas.save() canvas.translate(0f, canvas.height - backgroundHeight - bottomMargin) @@ -124,21 +119,26 @@ class TaskbarBackgroundRenderer(context: TaskbarActivityContext) { canvas.translate(0f, bottomMargin * ((1f - scaleFactor) / 2f)) // Draw shadow. - val shadowAlpha = mapToRange(paint.alpha.toFloat(), 0f, 255f, 0f, 25f, - Interpolators.LINEAR) - paint.setShadowLayer(shadowBlur, 0f, keyShadowDistance, + val shadowAlpha = + mapToRange(paint.alpha.toFloat(), 0f, 255f, 0f, 25f, Interpolators.LINEAR) + paint.setShadowLayer( + shadowBlur, + 0f, + keyShadowDistance, setColorAlphaBound(Color.BLACK, Math.round(shadowAlpha)) ) // Draw background. - val radius = backgroundHeight / 2f; + val radius = backgroundHeight / 2f canvas.drawRoundRect( transientBackgroundBounds.left + (delta / 2f), translationYForSwipe, transientBackgroundBounds.right - (delta / 2f), backgroundHeight + translationYForSwipe, - radius, radius, paint + radius, + radius, + paint ) } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt index a48b88fc19..9f245657d0 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt @@ -36,10 +36,8 @@ import com.android.launcher3.anim.AlphaUpdateListener import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController import java.io.PrintWriter -/** - * Handles the insets that Taskbar provides to underlying apps and the IME. - */ -class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTaskbarController { +/** Handles the insets that Taskbar provides to underlying apps and the IME. */ +class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTaskbarController { /** The bottom insets taskbar provides to the IME when IME is visible. */ val taskbarHeightForIme: Int = context.resources.getDimensionPixelSize(R.dimen.taskbar_ime_size) @@ -77,13 +75,19 @@ class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTask fun onTaskbarWindowHeightOrInsetsChanged() { val touchableHeight = controllers.taskbarStashController.touchableHeight - touchableRegion.set(0, windowLayoutParams.height - touchableHeight, - context.deviceProfile.widthPx, windowLayoutParams.height) + touchableRegion.set( + 0, + windowLayoutParams.height - touchableHeight, + context.deviceProfile.widthPx, + windowLayoutParams.height + ) val contentHeight = controllers.taskbarStashController.contentHeightToReportToApps val tappableHeight = controllers.taskbarStashController.tappableHeightToReportToApps for (provider in windowLayoutParams.providedInsets) { - if (provider.type == ITYPE_EXTRA_NAVIGATION_BAR - || provider.type == ITYPE_BOTTOM_MANDATORY_GESTURES) { + if ( + provider.type == ITYPE_EXTRA_NAVIGATION_BAR || + provider.type == ITYPE_BOTTOM_MANDATORY_GESTURES + ) { provider.insetsSize = getInsetsByNavMode(contentHeight) } else if (provider.type == ITYPE_BOTTOM_TAPPABLE_ELEMENT) { provider.insetsSize = getInsetsByNavMode(tappableHeight) @@ -91,24 +95,20 @@ class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTask } val imeInsetsSize = getInsetsByNavMode(taskbarHeightForIme) - val insetsSizeOverride = arrayOf( - InsetsFrameProvider.InsetsSizeOverride( - TYPE_INPUT_METHOD, - imeInsetsSize - ), - ) + val insetsSizeOverride = + arrayOf( + InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize), + ) // Use 0 tappableElement insets for the VoiceInteractionWindow when gesture nav is enabled. val visInsetsSizeForGestureNavTappableElement = getInsetsByNavMode(0) - val insetsSizeOverrideForGestureNavTappableElement = arrayOf( - InsetsFrameProvider.InsetsSizeOverride( - TYPE_INPUT_METHOD, - imeInsetsSize - ), - InsetsFrameProvider.InsetsSizeOverride( - TYPE_VOICE_INTERACTION, - visInsetsSizeForGestureNavTappableElement - ), - ) + val insetsSizeOverrideForGestureNavTappableElement = + arrayOf( + InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize), + InsetsFrameProvider.InsetsSizeOverride( + TYPE_VOICE_INTERACTION, + visInsetsSizeForGestureNavTappableElement + ), + ) for (provider in windowLayoutParams.providedInsets) { if (context.isGestureNav && provider.type == ITYPE_BOTTOM_TAPPABLE_ELEMENT) { provider.insetsSizeOverrides = insetsSizeOverrideForGestureNavTappableElement @@ -120,9 +120,11 @@ class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTask /** * @return [Insets] where the [bottomInset] is either used as a bottom inset or + * ``` * right/left inset if using 3 button nav + * ``` */ - private fun getInsetsByNavMode(bottomInset: Int) : Insets { + private fun getInsetsByNavMode(bottomInset: Int): Insets { val devicePortrait = !context.deviceProfile.isLandscape if (!TaskbarManager.isPhoneButtonNavMode(context) || devicePortrait) { // Taskbar or portrait phone mode @@ -139,9 +141,9 @@ class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTask * @param providesInsetsTypes The inset types we would like this layout params to provide. */ fun setProvidesInsetsTypes(params: WindowManager.LayoutParams, providesInsetsTypes: IntArray) { - params.providedInsets = arrayOfNulls(providesInsetsTypes.size); + params.providedInsets = arrayOfNulls(providesInsetsTypes.size) for (i in providesInsetsTypes.indices) { - params.providedInsets[i] = InsetsFrameProvider(providesInsetsTypes[i]); + params.providedInsets[i] = InsetsFrameProvider(providesInsetsTypes[i]) } } @@ -153,14 +155,17 @@ class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTask insetsInfo.touchableRegion.setEmpty() // Always have nav buttons be touchable controllers.navbarButtonsViewController.addVisibleButtonsRegion( - context.dragLayer, insetsInfo.touchableRegion + context.dragLayer, + insetsInfo.touchableRegion ) var insetsIsTouchableRegion = true if (context.dragLayer.alpha < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) { // Let touches pass through us. insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION) - } else if (controllers.navbarButtonsViewController.isImeVisible - && controllers.taskbarStashController.isStashed()) { + } else if ( + controllers.navbarButtonsViewController.isImeVisible && + controllers.taskbarStashController.isStashed() + ) { insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION) } else if (!controllers.uiController.isTaskbarTouchable) { // Let touches pass through us. @@ -174,9 +179,10 @@ class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTask insetsInfo.touchableRegion.set(touchableRegion) } insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION) - } else if (controllers.taskbarViewController.areIconsVisible() - || AbstractFloatingView.hasOpenView(context, AbstractFloatingView.TYPE_ALL) - || context.isNavBarKidsModeActive + } else if ( + controllers.taskbarViewController.areIconsVisible() || + AbstractFloatingView.hasOpenView(context, AbstractFloatingView.TYPE_ALL) || + context.isNavBarKidsModeActive ) { // Taskbar has some touchable elements, take over the full taskbar area insetsInfo.setTouchableInsets( @@ -198,8 +204,12 @@ class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTask pw.println(prefix + "TaskbarInsetsController:") pw.println("$prefix\twindowHeight=${windowLayoutParams.height}") for (provider in windowLayoutParams.providedInsets) { - pw.print("$prefix\tprovidedInsets: (type=" + InsetsState.typeToString(provider.type) - + " insetsSize=" + provider.insetsSize) + pw.print( + "$prefix\tprovidedInsets: (type=" + + InsetsState.typeToString(provider.type) + + " insetsSize=" + + provider.insetsSize + ) if (provider.insetsSizeOverrides != null) { pw.print(" insetsSizeOverrides={") for ((i, overrideSize) in provider.insetsSizeOverrides.withIndex()) { diff --git a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt index a033507b8e..be34fb160b 100644 --- a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt @@ -10,12 +10,9 @@ import java.io.PrintWriter private const val TASKBAR_ICONS_FADE_DURATION = 300L private const val STASHED_HANDLE_FADE_DURATION = 180L -/** - * Controls Taskbar behavior while Voice Interaction Window (assistant) is showing. - */ -class VoiceInteractionWindowController(val context: TaskbarActivityContext) - : TaskbarControllers.LoggableTaskbarController, - TaskbarControllers.BackgroundRendererController { +/** Controls Taskbar behavior while Voice Interaction Window (assistant) is showing. */ +class VoiceInteractionWindowController(val context: TaskbarActivityContext) : + TaskbarControllers.LoggableTaskbarController, TaskbarControllers.BackgroundRendererController { private val taskbarBackgroundRenderer = TaskbarBackgroundRenderer(context) @@ -37,8 +34,10 @@ class VoiceInteractionWindowController(val context: TaskbarActivityContext) override fun draw(canvas: Canvas) { super.draw(canvas) - if (this@VoiceInteractionWindowController.context.isGestureNav - && controllers.taskbarStashController.isInAppAndNotStashed) { + if ( + this@VoiceInteractionWindowController.context.isGestureNav && + controllers.taskbarStashController.isInAppAndNotStashed + ) { taskbarBackgroundRenderer.draw(canvas) } } @@ -46,8 +45,8 @@ class VoiceInteractionWindowController(val context: TaskbarActivityContext) separateWindowForTaskbarBackground.recreateControllers() separateWindowForTaskbarBackground.setWillNotDraw(false) - separateWindowLayoutParams = context.createDefaultWindowLayoutParams( - TYPE_APPLICATION_OVERLAY) + separateWindowLayoutParams = + context.createDefaultWindowLayoutParams(TYPE_APPLICATION_OVERLAY) separateWindowLayoutParams.isSystemApplicationOverlay = true } @@ -63,14 +62,16 @@ class VoiceInteractionWindowController(val context: TaskbarActivityContext) // Fade out taskbar icons and stashed handle. val taskbarIconAlpha = if (isVoiceInteractionWindowVisible) 0f else 1f - val fadeTaskbarIcons = controllers.taskbarViewController.taskbarIconAlpha - .get(TaskbarViewController.ALPHA_INDEX_ASSISTANT_INVOKED) - .animateToValue(taskbarIconAlpha) - .setDuration(TASKBAR_ICONS_FADE_DURATION) - val fadeStashedHandle = controllers.stashedHandleViewController.stashedHandleAlpha - .get(StashedHandleViewController.ALPHA_INDEX_ASSISTANT_INVOKED) - .animateToValue(taskbarIconAlpha) - .setDuration(STASHED_HANDLE_FADE_DURATION) + val fadeTaskbarIcons = + controllers.taskbarViewController.taskbarIconAlpha + .get(TaskbarViewController.ALPHA_INDEX_ASSISTANT_INVOKED) + .animateToValue(taskbarIconAlpha) + .setDuration(TASKBAR_ICONS_FADE_DURATION) + val fadeStashedHandle = + controllers.stashedHandleViewController.stashedHandleAlpha + .get(StashedHandleViewController.ALPHA_INDEX_ASSISTANT_INVOKED) + .animateToValue(taskbarIconAlpha) + .setDuration(STASHED_HANDLE_FADE_DURATION) fadeTaskbarIcons.start() fadeStashedHandle.start() if (skipAnim) { @@ -83,23 +84,30 @@ class VoiceInteractionWindowController(val context: TaskbarActivityContext) /** * Either: + * * Hides the TaskbarDragLayer background and creates a new window to draw just that background. + * * OR + * * Removes the temporary window and show the TaskbarDragLayer background again. */ private fun moveTaskbarBackgroundToAppropriateLayer(skipAnim: Boolean) { - val taskbarBackgroundOverride = controllers.taskbarDragLayerController - .overrideBackgroundAlpha + val taskbarBackgroundOverride = + controllers.taskbarDragLayerController.overrideBackgroundAlpha val moveToLowerLayer = isVoiceInteractionWindowVisible - val onWindowsSynchronized = if (moveToLowerLayer) { - // First add the temporary window, then hide the overlapping taskbar background. - context.addWindowView(separateWindowForTaskbarBackground, separateWindowLayoutParams); - { taskbarBackgroundOverride.updateValue(0f) } - } else { - // First reapply the original taskbar background, then remove the temporary window. - taskbarBackgroundOverride.updateValue(1f); - { context.removeWindowView(separateWindowForTaskbarBackground) } - } + val onWindowsSynchronized = + if (moveToLowerLayer) { + // First add the temporary window, then hide the overlapping taskbar background. + context.addWindowView( + separateWindowForTaskbarBackground, + separateWindowLayoutParams + ); + { taskbarBackgroundOverride.updateValue(0f) } + } else { + // First reapply the original taskbar background, then remove the temporary window. + taskbarBackgroundOverride.updateValue(1f); + { context.removeWindowView(separateWindowForTaskbarBackground) } + } if (skipAnim) { onWindowsSynchronized() @@ -121,4 +129,4 @@ class VoiceInteractionWindowController(val context: TaskbarActivityContext) pw.println(prefix + "VoiceInteractionWindowController:") pw.println("$prefix\tisVoiceInteractionWindowVisible=$isVoiceInteractionWindowVisible") } -} \ No newline at end of file +} diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt index 68ea27a8ec..a82902fffc 100644 --- a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt @@ -30,20 +30,17 @@ import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonL * [navButtonContainer] * * @property navButtonContainer ViewGroup that holds the 3 navigation buttons. - * @property endContextualContainer ViewGroup that holds the end contextual button (ex, IME dismiss). + * @property endContextualContainer ViewGroup that holds the end contextual button (ex, IME + * dismiss). * @property startContextualContainer ViewGroup that holds the start contextual button (ex, A11y). */ abstract class AbstractNavButtonLayoutter( - val resources: Resources, - val navButtonContainer: LinearLayout, - protected val endContextualContainer: ViewGroup, - protected val startContextualContainer: ViewGroup + val resources: Resources, + val navButtonContainer: LinearLayout, + protected val endContextualContainer: ViewGroup, + protected val startContextualContainer: ViewGroup ) : NavButtonLayoutter { - protected val homeButton: ImageView = navButtonContainer - .findViewById(R.id.home) - protected val recentsButton: ImageView = navButtonContainer - .findViewById(R.id.recent_apps) - protected val backButton: ImageView = navButtonContainer - .findViewById(R.id.back) + protected val homeButton: ImageView = navButtonContainer.findViewById(R.id.home) + protected val recentsButton: ImageView = navButtonContainer.findViewById(R.id.recent_apps) + protected val backButton: ImageView = navButtonContainer.findViewById(R.id.back) } - diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt index c67ab7964e..c093c9240a 100644 --- a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt @@ -35,56 +35,47 @@ import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DRAWABLE_SYS import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DRAWABLE_SYSBAR_HOME_KIDS class KidsNavLayoutter( - resources: Resources, - navBarContainer: LinearLayout, - endContextualContainer: ViewGroup, - startContextualContainer: ViewGroup -) : AbstractNavButtonLayoutter( + resources: Resources, + navBarContainer: LinearLayout, + endContextualContainer: ViewGroup, + startContextualContainer: ViewGroup +) : + AbstractNavButtonLayoutter( resources, navBarContainer, endContextualContainer, startContextualContainer -) { + ) { override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) { - val iconSize: Int = resources.getDimensionPixelSize( - DIMEN_TASKBAR_ICON_SIZE_KIDS) - val buttonWidth: Int = resources.getDimensionPixelSize( - DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS) - val buttonHeight: Int = resources.getDimensionPixelSize( - DIMEN_TASKBAR_NAV_BUTTONS_HEIGHT_KIDS) - val buttonRadius: Int = resources.getDimensionPixelSize( - DIMEN_TASKBAR_NAV_BUTTONS_CORNER_RADIUS_KIDS) + val iconSize: Int = resources.getDimensionPixelSize(DIMEN_TASKBAR_ICON_SIZE_KIDS) + val buttonWidth: Int = resources.getDimensionPixelSize(DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS) + val buttonHeight: Int = + resources.getDimensionPixelSize(DIMEN_TASKBAR_NAV_BUTTONS_HEIGHT_KIDS) + val buttonRadius: Int = + resources.getDimensionPixelSize(DIMEN_TASKBAR_NAV_BUTTONS_CORNER_RADIUS_KIDS) val paddingLeft = (buttonWidth - iconSize) / 2 val paddingTop = (buttonHeight - iconSize) / 2 // Update icons - backButton.setImageDrawable( - backButton.context.getDrawable(DRAWABLE_SYSBAR_BACK_KIDS)) + backButton.setImageDrawable(backButton.context.getDrawable(DRAWABLE_SYSBAR_BACK_KIDS)) backButton.scaleType = ImageView.ScaleType.FIT_CENTER backButton.setPadding(paddingLeft, paddingTop, paddingLeft, paddingTop) - homeButton.setImageDrawable( - homeButton.getContext().getDrawable(DRAWABLE_SYSBAR_HOME_KIDS)) + homeButton.setImageDrawable(homeButton.getContext().getDrawable(DRAWABLE_SYSBAR_HOME_KIDS)) homeButton.scaleType = ImageView.ScaleType.FIT_CENTER homeButton.setPadding(paddingLeft, paddingTop, paddingLeft, paddingTop) // Home button layout - val homeLayoutparams = LinearLayout.LayoutParams( - buttonWidth, - buttonHeight - ) - val homeButtonLeftMargin: Int = resources.getDimensionPixelSize( - DIMEN_TASKBAR_HOME_BUTTON_LEFT_MARGIN_KIDS) + val homeLayoutparams = LinearLayout.LayoutParams(buttonWidth, buttonHeight) + val homeButtonLeftMargin: Int = + resources.getDimensionPixelSize(DIMEN_TASKBAR_HOME_BUTTON_LEFT_MARGIN_KIDS) homeLayoutparams.setMargins(homeButtonLeftMargin, 0, 0, 0) homeButton.layoutParams = homeLayoutparams // Back button layout - val backLayoutParams = LinearLayout.LayoutParams( - buttonWidth, - buttonHeight - ) - val backButtonLeftMargin: Int = resources.getDimensionPixelSize( - DIMEN_TASKBAR_BACK_BUTTON_LEFT_MARGIN_KIDS) + val backLayoutParams = LinearLayout.LayoutParams(buttonWidth, buttonHeight) + val backButtonLeftMargin: Int = + resources.getDimensionPixelSize(DIMEN_TASKBAR_BACK_BUTTON_LEFT_MARGIN_KIDS) backLayoutParams.setMargins(backButtonLeftMargin, 0, 0, 0) backButton.layoutParams = backLayoutParams @@ -106,4 +97,4 @@ class KidsNavLayoutter( homeButton.onLongClickListener = null } -} \ No newline at end of file +} diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt index db0a2d8d44..2092721b92 100644 --- a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt @@ -30,10 +30,9 @@ import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonL /** * Select the correct layout for nav buttons * - * Since layouts are done dynamically for the nav buttons on Taskbar, this - * class returns a corresponding [NavButtonLayoutter] via - * [Companion.getUiLayoutter] - * that can help position the buttons based on the current [DeviceProfile] + * Since layouts are done dynamically for the nav buttons on Taskbar, this class returns a + * corresponding [NavButtonLayoutter] via [Companion.getUiLayoutter] that can help position the + * buttons based on the current [DeviceProfile] */ class NavButtonLayoutFactory { companion object { @@ -41,11 +40,10 @@ class NavButtonLayoutFactory { * Get the correct instance of [NavButtonLayoutter] * * No layouts supported for configurations where: - * * taskbar isn't showing AND - * * the device is not in [phoneMode] - * OR - * * phone is showing - * * device is using gesture navigation + * * taskbar isn't showing AND + * * the device is not in [phoneMode] OR + * * phone is showing + * * device is using gesture navigation * * @param navButtonsView ViewGroup that contains start, end, nav button ViewGroups * @param isKidsMode no-op when taskbar is hidden/not showing @@ -53,44 +51,64 @@ class NavButtonLayoutFactory { * @param phoneMode refers to the device using the taskbar window on phones * @param isThreeButtonNav are no-ops when taskbar is present/showing */ - fun getUiLayoutter(deviceProfile: DeviceProfile, - navButtonsView: FrameLayout, - resources: Resources, - isKidsMode: Boolean, - isInSetup: Boolean, - isThreeButtonNav: Boolean, - phoneMode: Boolean): - NavButtonLayoutter { - val navButtonContainer = - navButtonsView.findViewById(ID_END_NAV_BUTTONS) + fun getUiLayoutter( + deviceProfile: DeviceProfile, + navButtonsView: FrameLayout, + resources: Resources, + isKidsMode: Boolean, + isInSetup: Boolean, + isThreeButtonNav: Boolean, + phoneMode: Boolean + ): NavButtonLayoutter { + val navButtonContainer = navButtonsView.findViewById(ID_END_NAV_BUTTONS) val endContextualContainer = - navButtonsView.findViewById(ID_END_CONTEXTUAL_BUTTONS) + navButtonsView.findViewById(ID_END_CONTEXTUAL_BUTTONS) val startContextualContainer = - navButtonsView.findViewById(ID_START_CONTEXTUAL_BUTTONS) + navButtonsView.findViewById(ID_START_CONTEXTUAL_BUTTONS) val isPhoneNavMode = phoneMode && isThreeButtonNav return when { isPhoneNavMode -> { if (!deviceProfile.isLandscape) { - PhonePortraitNavLayoutter(resources, navButtonContainer, - endContextualContainer, startContextualContainer) + PhonePortraitNavLayoutter( + resources, + navButtonContainer, + endContextualContainer, + startContextualContainer + ) } else { - PhoneLandscapeNavLayoutter(resources, navButtonContainer, - endContextualContainer, startContextualContainer) + PhoneLandscapeNavLayoutter( + resources, + navButtonContainer, + endContextualContainer, + startContextualContainer + ) } } deviceProfile.isTaskbarPresent -> { return when { isInSetup -> { - SetupNavLayoutter(resources, navButtonContainer, endContextualContainer, - startContextualContainer) + SetupNavLayoutter( + resources, + navButtonContainer, + endContextualContainer, + startContextualContainer + ) } isKidsMode -> { - KidsNavLayoutter(resources, navButtonContainer, endContextualContainer, - startContextualContainer) + KidsNavLayoutter( + resources, + navButtonContainer, + endContextualContainer, + startContextualContainer + ) } else -> - TaskbarNavLayoutter(resources, navButtonContainer, endContextualContainer, - startContextualContainer) + TaskbarNavLayoutter( + resources, + navButtonContainer, + endContextualContainer, + startContextualContainer + ) } } else -> error("No layoutter found") @@ -98,8 +116,8 @@ class NavButtonLayoutFactory { } } - /** Lays out and provides access to the home, recents, and back buttons for various mischief */ + /** Lays out and provides access to the home, recents, and back buttons for various mischief */ interface NavButtonLayoutter { fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) } -} \ No newline at end of file +} diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt index a89476e15a..201895fc67 100644 --- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt @@ -28,24 +28,24 @@ import com.android.launcher3.taskbar.TaskbarManager import com.android.launcher3.util.DimensionUtils class PhoneLandscapeNavLayoutter( - resources: Resources, - navBarContainer: LinearLayout, - endContextualContainer: ViewGroup, - startContextualContainer: ViewGroup -) : AbstractNavButtonLayoutter( + resources: Resources, + navBarContainer: LinearLayout, + endContextualContainer: ViewGroup, + startContextualContainer: ViewGroup +) : + AbstractNavButtonLayoutter( resources, navBarContainer, endContextualContainer, startContextualContainer -) { + ) { override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) { // TODO(b/230395757): Polish pending, this is just to make it usable val navContainerParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams - val endStartMargins = - resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) - val taskbarDimensions = DimensionUtils.getTaskbarPhoneDimensions(dp, resources, - TaskbarManager.isPhoneMode(dp)) + val endStartMargins = resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) + val taskbarDimensions = + DimensionUtils.getTaskbarPhoneDimensions(dp, resources, TaskbarManager.isPhoneMode(dp)) navButtonContainer.removeAllViews() navButtonContainer.orientation = LinearLayout.VERTICAL @@ -68,7 +68,7 @@ class PhoneLandscapeNavLayoutter( // Add the spaces in between the nav buttons val spaceInBetween: Int = - resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone) + resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone) navButtonContainer.children.forEachIndexed { i, navButton -> val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams buttonLayoutParams.weight = 1f @@ -86,4 +86,4 @@ class PhoneLandscapeNavLayoutter( } } } -} \ No newline at end of file +} diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt index 275f59faef..f7ac9745f0 100644 --- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt @@ -26,17 +26,24 @@ import com.android.launcher3.R import com.android.launcher3.taskbar.TaskbarManager import com.android.launcher3.util.DimensionUtils -class PhonePortraitNavLayoutter(resources: Resources, navBarContainer: LinearLayout, - endContextualContainer: ViewGroup, - startContextualContainer: ViewGroup) : - AbstractNavButtonLayoutter(resources, navBarContainer, endContextualContainer, - startContextualContainer) { +class PhonePortraitNavLayoutter( + resources: Resources, + navBarContainer: LinearLayout, + endContextualContainer: ViewGroup, + startContextualContainer: ViewGroup +) : + AbstractNavButtonLayoutter( + resources, + navBarContainer, + endContextualContainer, + startContextualContainer + ) { override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) { // TODO(b/230395757): Polish pending, this is just to make it usable val navContainerParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams - val taskbarDimensions = DimensionUtils.getTaskbarPhoneDimensions(dp, resources, - TaskbarManager.isPhoneMode(dp)) + val taskbarDimensions = + DimensionUtils.getTaskbarPhoneDimensions(dp, resources, TaskbarManager.isPhoneMode(dp)) val endStartMargins = resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) navContainerParams.width = taskbarDimensions.x navContainerParams.height = ViewGroup.LayoutParams.MATCH_PARENT @@ -58,7 +65,7 @@ class PhonePortraitNavLayoutter(resources: Resources, navBarContainer: LinearLay // Add the spaces in between the nav buttons val spaceInBetween = - resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone) + resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone) for (i in 0 until navButtonContainer.childCount) { val navButton = navButtonContainer.getChildAt(i) val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams @@ -80,4 +87,4 @@ class PhonePortraitNavLayoutter(resources: Resources, navBarContainer: LinearLay } } } -} \ No newline at end of file +} diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt index afe70d6c21..a24002c44d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt @@ -24,16 +24,17 @@ import android.widget.LinearLayout import com.android.launcher3.DeviceProfile class SetupNavLayoutter( - resources: Resources, - navButtonContainer: LinearLayout, - endContextualContainer: ViewGroup, - startContextualContainer: ViewGroup -) : AbstractNavButtonLayoutter( + resources: Resources, + navButtonContainer: LinearLayout, + endContextualContainer: ViewGroup, + startContextualContainer: ViewGroup +) : + AbstractNavButtonLayoutter( resources, navButtonContainer, endContextualContainer, startContextualContainer -) { + ) { override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) { // Since setup wizard only has back button enabled, it looks strange to be @@ -46,4 +47,4 @@ class SetupNavLayoutter( } navButtonContainer.requestLayout() } -} \ No newline at end of file +} diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt index b2ca2afa04..5ec7ca0e2e 100644 --- a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt +++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt @@ -24,20 +24,19 @@ import android.widget.LinearLayout import com.android.launcher3.DeviceProfile import com.android.launcher3.R -/** - * Layoutter for showing 3 button navigation on large screen - */ +/** Layoutter for showing 3 button navigation on large screen */ class TaskbarNavLayoutter( - resources: Resources, - navBarContainer: LinearLayout, - endContextualContainer: ViewGroup, - startContextualContainer: ViewGroup -) : AbstractNavButtonLayoutter( + resources: Resources, + navBarContainer: LinearLayout, + endContextualContainer: ViewGroup, + startContextualContainer: ViewGroup +) : + AbstractNavButtonLayoutter( resources, navBarContainer, endContextualContainer, startContextualContainer -) { + ) { override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) { // Add spacing after the end of the last nav button @@ -80,4 +79,4 @@ class TaskbarNavLayoutter( } } } -} \ No newline at end of file +} diff --git a/quickstep/src/com/android/quickstep/util/LogUtils.kt b/quickstep/src/com/android/quickstep/util/LogUtils.kt index bad8506eec..300c4a1c24 100644 --- a/quickstep/src/com/android/quickstep/util/LogUtils.kt +++ b/quickstep/src/com/android/quickstep/util/LogUtils.kt @@ -20,15 +20,14 @@ import com.android.internal.logging.InstanceIdSequence import com.android.launcher3.logging.InstanceId object LogUtils { - /** - * @return a [Pair] of two InstanceIds but with different types, one that can be used by framework - * (if needing to pass through an intent or such) and one used in Launcher - */ - @JvmStatic - fun getShellShareableInstanceId(): - Pair { - val internalInstanceId = InstanceIdSequence(InstanceId.INSTANCE_ID_MAX).newInstanceId() - val launcherInstanceId = InstanceId(internalInstanceId.id) - return Pair(internalInstanceId, launcherInstanceId) - } + /** + * @return a [Pair] of two InstanceIds but with different types, one that can be used by + * framework (if needing to pass through an intent or such) and one used in Launcher + */ + @JvmStatic + fun getShellShareableInstanceId(): Pair { + val internalInstanceId = InstanceIdSequence(InstanceId.INSTANCE_ID_MAX).newInstanceId() + val launcherInstanceId = InstanceId(internalInstanceId.id) + return Pair(internalInstanceId, launcherInstanceId) + } } diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt index bdc0585f83..54b58b983d 100644 --- a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt +++ b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt @@ -48,13 +48,15 @@ class TaskMenuViewWithArrow : ArrowPopup { taskContainer: TaskIdAttributeContainer, alignSecondRow: Boolean = false ): Boolean { - val activity = BaseDraggingActivity - .fromContext(taskContainer.taskView.context) - val taskMenuViewWithArrow = activity.layoutInflater - .inflate( - R.layout.task_menu_with_arrow, - activity.dragLayer, - false + val activity = + BaseDraggingActivity.fromContext( + taskContainer.taskView.context + ) + val taskMenuViewWithArrow = + activity.layoutInflater.inflate( + R.layout.task_menu_with_arrow, + activity.dragLayer, + false ) as TaskMenuViewWithArrow<*> return taskMenuViewWithArrow.populateAndShowForTask(taskContainer, alignSecondRow) @@ -63,11 +65,11 @@ class TaskMenuViewWithArrow : ArrowPopup { constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet) : super(context, attrs) - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super( - context, - attrs, - defStyleAttr - ) + constructor( + context: Context, + attrs: AttributeSet, + defStyleAttr: Int + ) : super(context, attrs, defStyleAttr) init { clipToOutline = true @@ -91,10 +93,10 @@ class TaskMenuViewWithArrow : ArrowPopup { private var optionMeasuredHeight = 0 private val arrowHorizontalPadding: Int - get() = if (taskView.isFocusedTask) - resources.getDimensionPixelSize(R.dimen.task_menu_horizontal_padding) - else - 0 + get() = + if (taskView.isFocusedTask) + resources.getDimensionPixelSize(R.dimen.task_menu_horizontal_padding) + else 0 private var iconView: IconView? = null private var scrim: View? = null @@ -139,19 +141,20 @@ class TaskMenuViewWithArrow : ArrowPopup { } private fun addScrim() { - scrim = View(context).apply { - layoutParams = FrameLayout.LayoutParams( - FrameLayout.LayoutParams.MATCH_PARENT, - FrameLayout.LayoutParams.MATCH_PARENT - ) - setBackgroundColor(Themes.getAttrColor(context, R.attr.overviewScrimColor)) - alpha = 0f - } + scrim = + View(context).apply { + layoutParams = + FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT + ) + setBackgroundColor(Themes.getAttrColor(context, R.attr.overviewScrimColor)) + alpha = 0f + } popupContainer.addView(scrim) } - /** @return true if successfully able to populate task view menu, false otherwise - */ + /** @return true if successfully able to populate task view menu, false otherwise */ private fun populateMenu(): Boolean { // Icon may not be loaded if (taskContainer.task.icon == null) return false @@ -162,9 +165,9 @@ class TaskMenuViewWithArrow : ArrowPopup { private fun addMenuOptions() { // Add the options - TaskOverlayFactory - .getEnabledShortcuts(taskView, taskContainer) - .forEach { this.addMenuOption(it) } + TaskOverlayFactory.getEnabledShortcuts(taskView, taskContainer).forEach { + this.addMenuOption(it) + } // Add the spaces between items val divider = ShapeDrawable(RectShape()) @@ -185,9 +188,9 @@ class TaskMenuViewWithArrow : ArrowPopup { } private fun addMenuOption(menuOption: SystemShortcut<*>) { - val menuOptionView = mActivityContext.layoutInflater.inflate( - R.layout.task_view_menu_option, this, false - ) as LinearLayout + val menuOptionView = + mActivityContext.layoutInflater.inflate(R.layout.task_view_menu_option, this, false) + as LinearLayout menuOption.setIconAndLabelFor( menuOptionView.findViewById(R.id.icon), menuOptionView.findViewById(R.id.text) @@ -230,24 +233,25 @@ class TaskMenuViewWithArrow : ArrowPopup { } /** - * Copy the iconView from taskView to dragLayer so it can stay on top of the scrim. - * It needs to be called after [getTargetObjectLocation] because [mTempRect] needs to be - * populated. + * Copy the iconView from taskView to dragLayer so it can stay on top of the scrim. It needs to + * be called after [getTargetObjectLocation] because [mTempRect] needs to be populated. */ private fun copyIconToDragLayer(insets: Rect) { - iconView = IconView(context).apply { - layoutParams = FrameLayout.LayoutParams( - taskContainer.iconView.width, - taskContainer.iconView.height - ) - x = mTempRect.left.toFloat() - insets.left - y = mTempRect.top.toFloat() - insets.top - drawable = taskContainer.iconView.drawable - setDrawableSize( - taskContainer.iconView.drawableWidth, - taskContainer.iconView.drawableHeight - ) - } + iconView = + IconView(context).apply { + layoutParams = + FrameLayout.LayoutParams( + taskContainer.iconView.width, + taskContainer.iconView.height + ) + x = mTempRect.left.toFloat() - insets.left + y = mTempRect.top.toFloat() - insets.top + drawable = taskContainer.iconView.drawable + setDrawableSize( + taskContainer.iconView.drawableWidth, + taskContainer.iconView.drawableHeight + ) + } popupContainer.addView(iconView) } @@ -281,12 +285,13 @@ class TaskMenuViewWithArrow : ArrowPopup { // which means the arrow is left aligned with the menu val rightAlignedMenuStartX = mTempRect.left - widthWithArrow val leftAlignedMenuStartX = mTempRect.right + extraHorizontalSpace - mIsLeftAligned = if (mIsRtl) { - rightAlignedMenuStartX + insets.left < 0 - } else { - leftAlignedMenuStartX + (widthWithArrow - extraHorizontalSpace) + insets.left < + mIsLeftAligned = + if (mIsRtl) { + rightAlignedMenuStartX + insets.left < 0 + } else { + leftAlignedMenuStartX + (widthWithArrow - extraHorizontalSpace) + insets.left < dragLayer.width - insets.right - } + } var menuStartX = if (mIsLeftAligned) leftAlignedMenuStartX else rightAlignedMenuStartX @@ -311,8 +316,8 @@ class TaskMenuViewWithArrow : ArrowPopup { override fun addArrow() { popupContainer.addView(mArrow) mArrow.x = getArrowX() - mArrow.y = y + (optionMeasuredHeight / 2) - (mArrowHeight / 2) + - extraSpaceForSecondRowAlignment + mArrow.y = + y + (optionMeasuredHeight / 2) - (mArrowHeight / 2) + extraSpaceForSecondRowAlignment updateArrowColor() @@ -322,22 +327,19 @@ class TaskMenuViewWithArrow : ArrowPopup { } private fun getArrowX(): Float { - return if (mIsLeftAligned) - x - mArrowHeight - else - x + measuredWidth + mArrowOffsetVertical + return if (mIsLeftAligned) x - mArrowHeight else x + measuredWidth + mArrowOffsetVertical } override fun updateArrowColor() { - mArrow.background = RoundedArrowDrawable( - mArrowWidth.toFloat(), - mArrowHeight.toFloat(), - mArrowPointRadius.toFloat(), - mIsLeftAligned, - mArrowColor - ) + mArrow.background = + RoundedArrowDrawable( + mArrowWidth.toFloat(), + mArrowHeight.toFloat(), + mArrowPointRadius.toFloat(), + mIsLeftAligned, + mArrowColor + ) elevation = mElevation mArrow.elevation = mElevation } - -} \ No newline at end of file +} diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt index 58f0949213..236b5db10c 100644 --- a/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt +++ b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt @@ -8,38 +8,29 @@ import android.widget.ImageView import android.widget.LinearLayout import androidx.test.runner.AndroidJUnit4 import com.android.launcher3.DeviceProfile +import com.android.launcher3.R import com.android.launcher3.taskbar.TaskbarManager +import java.lang.IllegalStateException +import org.junit.Assume.assumeTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import com.android.launcher3.R -import org.junit.Assume.assumeTrue import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations -import java.lang.IllegalStateException @RunWith(AndroidJUnit4::class) class NavButtonLayoutFactoryTest { - @Mock - lateinit var mockDeviceProfile: DeviceProfile - @Mock - lateinit var mockParentButtonContainer: FrameLayout - @Mock - lateinit var mockNavLayout: LinearLayout - @Mock - lateinit var mockStartContextualLayout: ViewGroup - @Mock - lateinit var mockEndContextualLayout: ViewGroup - @Mock - lateinit var mockResources: Resources - @Mock - lateinit var mockBackButton: ImageView - @Mock - lateinit var mockRecentsButton: ImageView - @Mock - lateinit var mockHomeButton: ImageView + @Mock lateinit var mockDeviceProfile: DeviceProfile + @Mock lateinit var mockParentButtonContainer: FrameLayout + @Mock lateinit var mockNavLayout: LinearLayout + @Mock lateinit var mockStartContextualLayout: ViewGroup + @Mock lateinit var mockEndContextualLayout: ViewGroup + @Mock lateinit var mockResources: Resources + @Mock lateinit var mockBackButton: ImageView + @Mock lateinit var mockRecentsButton: ImageView + @Mock lateinit var mockHomeButton: ImageView @Before fun setup() { @@ -53,11 +44,11 @@ class NavButtonLayoutFactoryTest { // Init top level layout whenever(mockParentButtonContainer.findViewById(R.id.end_nav_buttons)) - .thenReturn(mockNavLayout) + .thenReturn(mockNavLayout) whenever(mockParentButtonContainer.findViewById(R.id.end_contextual_buttons)) - .thenReturn(mockEndContextualLayout) + .thenReturn(mockEndContextualLayout) whenever(mockParentButtonContainer.findViewById(R.id.start_contextual_buttons)) - .thenReturn(mockStartContextualLayout) + .thenReturn(mockStartContextualLayout) } @Test @@ -65,8 +56,12 @@ class NavButtonLayoutFactoryTest { assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) mockDeviceProfile.isTaskbarPresent = true val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = - getLayoutter(isKidsMode = true, isInSetup = false, isThreeButtonNav = false, - phoneMode = false) + getLayoutter( + isKidsMode = true, + isInSetup = false, + isThreeButtonNav = false, + phoneMode = false + ) assert(layoutter is KidsNavLayoutter) } @@ -75,8 +70,12 @@ class NavButtonLayoutFactoryTest { assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) mockDeviceProfile.isTaskbarPresent = true val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = - getLayoutter(isKidsMode = false, isInSetup = true, isThreeButtonNav = false, - phoneMode = false) + getLayoutter( + isKidsMode = false, + isInSetup = true, + isThreeButtonNav = false, + phoneMode = false + ) assert(layoutter is SetupNavLayoutter) } @@ -85,8 +84,12 @@ class NavButtonLayoutFactoryTest { assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) mockDeviceProfile.isTaskbarPresent = true val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = - getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = false, - phoneMode = false) + getLayoutter( + isKidsMode = false, + isInSetup = false, + isThreeButtonNav = false, + phoneMode = false + ) assert(layoutter is TaskbarNavLayoutter) } @@ -94,8 +97,12 @@ class NavButtonLayoutFactoryTest { fun noValidLayoutForLargeScreenTaskbarNotPresent() { assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) mockDeviceProfile.isTaskbarPresent = false - getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = false, - phoneMode = false) + getLayoutter( + isKidsMode = false, + isInSetup = false, + isThreeButtonNav = false, + phoneMode = false + ) } @Test @@ -103,8 +110,12 @@ class NavButtonLayoutFactoryTest { assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) mockDeviceProfile.isTaskbarPresent = false val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = - getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = true, - phoneMode = true) + getLayoutter( + isKidsMode = false, + isInSetup = false, + isThreeButtonNav = true, + phoneMode = true + ) assert(layoutter is PhonePortraitNavLayoutter) } @@ -114,8 +125,12 @@ class NavButtonLayoutFactoryTest { mockDeviceProfile.isTaskbarPresent = false setDeviceProfileLandscape() val layoutter: NavButtonLayoutFactory.NavButtonLayoutter = - getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = true, - phoneMode = true) + getLayoutter( + isKidsMode = false, + isInSetup = false, + isThreeButtonNav = true, + phoneMode = true + ) assert(layoutter is PhoneLandscapeNavLayoutter) } @@ -123,8 +138,12 @@ class NavButtonLayoutFactoryTest { fun noValidLayoutForPhoneGestureNav() { assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) mockDeviceProfile.isTaskbarPresent = false - getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = false, - phoneMode = true) + getLayoutter( + isKidsMode = false, + isInSetup = false, + isThreeButtonNav = false, + phoneMode = true + ) } private fun setDeviceProfileLandscape() { @@ -134,15 +153,20 @@ class NavButtonLayoutFactoryTest { landscapeField.set(mockDeviceProfile, true) } - private fun getLayoutter(isKidsMode: Boolean, isInSetup: Boolean, - isThreeButtonNav: Boolean, phoneMode: Boolean): - NavButtonLayoutFactory.NavButtonLayoutter { + private fun getLayoutter( + isKidsMode: Boolean, + isInSetup: Boolean, + isThreeButtonNav: Boolean, + phoneMode: Boolean + ): NavButtonLayoutFactory.NavButtonLayoutter { return NavButtonLayoutFactory.getUiLayoutter( - deviceProfile = mockDeviceProfile, - navButtonsView = mockParentButtonContainer, - resources = mockResources, - isKidsMode = isKidsMode, isInSetup = isInSetup, - isThreeButtonNav = isThreeButtonNav, phoneMode = phoneMode + deviceProfile = mockDeviceProfile, + navButtonsView = mockParentButtonContainer, + resources = mockResources, + isKidsMode = isKidsMode, + isInSetup = isInSetup, + isThreeButtonNav = isThreeButtonNav, + phoneMode = phoneMode ) } -} \ No newline at end of file +} diff --git a/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt index 5ec935fee9..9afd893894 100644 --- a/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt +++ b/quickstep/tests/src/com/android/quickstep/FullscreenDrawParamsTest.kt @@ -27,15 +27,13 @@ import com.android.systemui.shared.recents.model.ThumbnailData import com.android.systemui.shared.recents.utilities.PreviewPositionHelper import com.android.wm.shell.util.SplitBounds import com.google.common.truth.Truth.assertThat +import kotlin.math.roundToInt import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.mock -import kotlin.math.roundToInt -/** - * Test for FullscreenDrawParams class. - */ +/** Test for FullscreenDrawParams class. */ @SmallTest @RunWith(AndroidJUnit4::class) class FullscreenDrawParamsTest : DeviceProfileBaseTest() { @@ -61,15 +59,29 @@ class FullscreenDrawParamsTest : DeviceProfileBaseTest() { val currentRotation = 0 val isRtl = false - mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth, - canvasHeight, dp.widthPx, dp.heightPx, dp.taskbarSize, dp.isTablet, currentRotation, - isRtl) - params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f, - /* taskViewScale= */ 1.0f, /* previewWidth= */ 0, dp, mPreviewPositionHelper) + mPreviewPositionHelper.updateThumbnailMatrix( + previewRect, + mThumbnailData, + canvasWidth, + canvasHeight, + dp.widthPx, + dp.heightPx, + dp.taskbarSize, + dp.isTablet, + currentRotation, + isRtl + ) + params.setProgress( + /* fullscreenProgress= */ 1.0f, + /* parentScale= */ 1.0f, + /* taskViewScale= */ 1.0f, + /* previewWidth= */ 0, + dp, + mPreviewPositionHelper + ) val expectedClippedInsets = RectF(0f, 0f, 0f, dp.taskbarSize * TASK_SCALE) - assertThat(params.mCurrentDrawnInsets) - .isEqualTo(expectedClippedInsets) + assertThat(params.mCurrentDrawnInsets).isEqualTo(expectedClippedInsets) } @Test @@ -83,25 +95,42 @@ class FullscreenDrawParamsTest : DeviceProfileBaseTest() { val isRtl = false // portrait/vertical split apps val dividerSize = 10 - val splitBounds = SplitBounds( + val splitBounds = + SplitBounds( Rect(0, 0, dp.widthPx, (dp.heightPx - dividerSize) / 2), Rect(0, (dp.heightPx + dividerSize) / 2, dp.widthPx, dp.heightPx), - 0 /*lefTopTaskId*/, 0 /*rightBottomTaskId*/) + 0 /*lefTopTaskId*/, + 0 /*rightBottomTaskId*/ + ) mPreviewPositionHelper.setSplitBounds(splitBounds, STAGE_POSITION_BOTTOM_OR_RIGHT) - mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth, - canvasHeight, dp.widthPx, dp.heightPx, dp.taskbarSize, dp.isTablet, currentRotation, - isRtl) - params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f, - /* taskViewScale= */ 1.0f, /* previewWidth= */ 0, dp, mPreviewPositionHelper) + mPreviewPositionHelper.updateThumbnailMatrix( + previewRect, + mThumbnailData, + canvasWidth, + canvasHeight, + dp.widthPx, + dp.heightPx, + dp.taskbarSize, + dp.isTablet, + currentRotation, + isRtl + ) + params.setProgress( + /* fullscreenProgress= */ 1.0f, + /* parentScale= */ 1.0f, + /* taskViewScale= */ 1.0f, + /* previewWidth= */ 0, + dp, + mPreviewPositionHelper + ) // Probably unhelpful, but also unclear how to test otherwise ¯\_(ツ)_/¯ - val fullscreenTaskHeight = dp.heightPx * - (1 - (splitBounds.topTaskPercent + splitBounds.dividerHeightPercent)) + val fullscreenTaskHeight = + dp.heightPx * (1 - (splitBounds.topTaskPercent + splitBounds.dividerHeightPercent)) val canvasScreenRatio = canvasHeight / fullscreenTaskHeight val expectedBottomHint = dp.taskbarSize * canvasScreenRatio - assertThat(params.mCurrentDrawnInsets.bottom) - .isWithin(1f).of(expectedBottomHint) + assertThat(params.mCurrentDrawnInsets.bottom).isWithin(1f).of(expectedBottomHint) } @Test @@ -115,20 +144,37 @@ class FullscreenDrawParamsTest : DeviceProfileBaseTest() { val isRtl = false // portrait/vertical split apps val dividerSize = 10 - val splitBounds = SplitBounds( + val splitBounds = + SplitBounds( Rect(0, 0, dp.widthPx, (dp.heightPx - dividerSize) / 2), Rect(0, (dp.heightPx + dividerSize) / 2, dp.widthPx, dp.heightPx), - 0 /*lefTopTaskId*/, 0 /*rightBottomTaskId*/) + 0 /*lefTopTaskId*/, + 0 /*rightBottomTaskId*/ + ) mPreviewPositionHelper.setSplitBounds(splitBounds, STAGE_POSITION_TOP_OR_LEFT) - mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth, - canvasHeight, dp.widthPx, dp.heightPx, dp.taskbarSize, dp.isTablet, currentRotation, - isRtl) - params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f, - /* taskViewScale= */ 1.0f, /* previewWidth= */ 0, dp, mPreviewPositionHelper) + mPreviewPositionHelper.updateThumbnailMatrix( + previewRect, + mThumbnailData, + canvasWidth, + canvasHeight, + dp.widthPx, + dp.heightPx, + dp.taskbarSize, + dp.isTablet, + currentRotation, + isRtl + ) + params.setProgress( + /* fullscreenProgress= */ 1.0f, + /* parentScale= */ 1.0f, + /* taskViewScale= */ 1.0f, + /* previewWidth= */ 0, + dp, + mPreviewPositionHelper + ) - assertThat(params.mCurrentDrawnInsets.bottom) - .isWithin(1f).of((0f)) + assertThat(params.mCurrentDrawnInsets.bottom).isWithin(1f).of((0f)) } @Test @@ -142,20 +188,37 @@ class FullscreenDrawParamsTest : DeviceProfileBaseTest() { val isRtl = false // portrait/vertical split apps val dividerSize = 10 - val splitBounds = SplitBounds( + val splitBounds = + SplitBounds( Rect(0, 0, (dp.widthPx - dividerSize) / 2, dp.heightPx), Rect((dp.widthPx + dividerSize) / 2, 0, dp.widthPx, dp.heightPx), - 0 /*lefTopTaskId*/, 0 /*rightBottomTaskId*/) + 0 /*lefTopTaskId*/, + 0 /*rightBottomTaskId*/ + ) mPreviewPositionHelper.setSplitBounds(splitBounds, STAGE_POSITION_BOTTOM_OR_RIGHT) - mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth, - canvasHeight, dp.widthPx, dp.heightPx, dp.taskbarSize, dp.isTablet, currentRotation, - isRtl) - params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f, - /* taskViewScale= */ 1.0f, /* previewWidth= */ 0, dp, mPreviewPositionHelper) + mPreviewPositionHelper.updateThumbnailMatrix( + previewRect, + mThumbnailData, + canvasWidth, + canvasHeight, + dp.widthPx, + dp.heightPx, + dp.taskbarSize, + dp.isTablet, + currentRotation, + isRtl + ) + params.setProgress( + /* fullscreenProgress= */ 1.0f, + /* parentScale= */ 1.0f, + /* taskViewScale= */ 1.0f, + /* previewWidth= */ 0, + dp, + mPreviewPositionHelper + ) - assertThat(params.mCurrentDrawnInsets.bottom) - .isWithin(1f).of((dp.taskbarSize * TASK_SCALE)) + assertThat(params.mCurrentDrawnInsets.bottom).isWithin(1f).of((dp.taskbarSize * TASK_SCALE)) } @Test @@ -168,14 +231,28 @@ class FullscreenDrawParamsTest : DeviceProfileBaseTest() { val currentRotation = 0 val isRtl = false - mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth, - canvasHeight, dp.widthPx, dp.heightPx, dp.taskbarSize, dp.isTablet, currentRotation, - isRtl) - params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f, - /* taskViewScale= */ 1.0f, /* previewWidth= */ 0, dp, mPreviewPositionHelper) + mPreviewPositionHelper.updateThumbnailMatrix( + previewRect, + mThumbnailData, + canvasWidth, + canvasHeight, + dp.widthPx, + dp.heightPx, + dp.taskbarSize, + dp.isTablet, + currentRotation, + isRtl + ) + params.setProgress( + /* fullscreenProgress= */ 1.0f, + /* parentScale= */ 1.0f, + /* taskViewScale= */ 1.0f, + /* previewWidth= */ 0, + dp, + mPreviewPositionHelper + ) val expectedClippedInsets = RectF(0f, 0f, 0f, 0f) - assertThat(params.mCurrentDrawnInsets) - .isEqualTo(expectedClippedInsets) + assertThat(params.mCurrentDrawnInsets).isEqualTo(expectedClippedInsets) } -} \ No newline at end of file +} diff --git a/quickstep/tests/src/com/android/quickstep/HotseatWidthCalculationTest.kt b/quickstep/tests/src/com/android/quickstep/HotseatWidthCalculationTest.kt index 4837c6c868..adbca32fba 100644 --- a/quickstep/tests/src/com/android/quickstep/HotseatWidthCalculationTest.kt +++ b/quickstep/tests/src/com/android/quickstep/HotseatWidthCalculationTest.kt @@ -29,8 +29,8 @@ import org.junit.runner.RunWith class HotseatWidthCalculationTest : DeviceProfileBaseTest() { /** - * This is a case when after setting the hotseat, the space needs to be recalculated - * but it doesn't need to change QSB width or remove icons + * This is a case when after setting the hotseat, the space needs to be recalculated but it + * doesn't need to change QSB width or remove icons */ @Test fun distribute_border_space_when_space_is_enough_portrait() { @@ -51,8 +51,8 @@ class HotseatWidthCalculationTest : DeviceProfileBaseTest() { } /** - * This is a case when after setting the hotseat, and recalculating spaces - * it still needs to remove icons for everything to fit + * This is a case when after setting the hotseat, and recalculating spaces it still needs to + * remove icons for everything to fit */ @Test fun decrease_num_of_icons_when_not_enough_space_portrait() { @@ -73,8 +73,8 @@ class HotseatWidthCalculationTest : DeviceProfileBaseTest() { } /** - * This is a case when after setting the hotseat, the space needs to be recalculated - * but it doesn't need to change QSB width or remove icons + * This is a case when after setting the hotseat, the space needs to be recalculated but it + * doesn't need to change QSB width or remove icons */ @Test fun distribute_border_space_when_space_is_enough_landscape() { @@ -94,8 +94,8 @@ class HotseatWidthCalculationTest : DeviceProfileBaseTest() { } /** - * This is a case when the hotseat spans a certain amount of columns - * and the nav buttons push the hotseat to the side, but not enough to change the border space. + * This is a case when the hotseat spans a certain amount of columns and the nav buttons push + * the hotseat to the side, but not enough to change the border space. */ @Test fun nav_buttons_dont_interfere_with_required_hotseat_width() { @@ -118,9 +118,7 @@ class HotseatWidthCalculationTest : DeviceProfileBaseTest() { assertThat(dp.hotseatQsbWidth).isEqualTo(1233) } - /** - * This is a case when after setting the hotseat, the QSB width needs to be changed to fit - */ + /** This is a case when after setting the hotseat, the QSB width needs to be changed to fit */ @Test fun decrease_qsb_when_not_enough_space_landscape() { initializeVarsForTablet(isGestureMode = false, isLandscape = true) diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt index 23ff10ad41..0d6ed04f2a 100644 --- a/src/com/android/launcher3/LauncherPrefs.kt +++ b/src/com/android/launcher3/LauncherPrefs.kt @@ -9,12 +9,17 @@ object LauncherPrefs { fun getPrefs(context: Context): SharedPreferences { // Use application context for shared preferences, so that we use a single cached instance return context.applicationContext.getSharedPreferences( - LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE) + LauncherFiles.SHARED_PREFERENCES_KEY, + Context.MODE_PRIVATE + ) } @JvmStatic fun getDevicePrefs(context: Context): SharedPreferences { // Use application context for shared preferences, so that we use a single cached instance return context.applicationContext.getSharedPreferences( - LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE) - }} \ No newline at end of file + LauncherFiles.DEVICE_PREFERENCES_KEY, + Context.MODE_PRIVATE + ) + } +} diff --git a/src/com/android/launcher3/util/DimensionUtils.kt b/src/com/android/launcher3/util/DimensionUtils.kt index 758b3a962f..1922310b28 100644 --- a/src/com/android/launcher3/util/DimensionUtils.kt +++ b/src/com/android/launcher3/util/DimensionUtils.kt @@ -24,12 +24,15 @@ import com.android.launcher3.R object DimensionUtils { /** - * Point where x is width, and y is height of taskbar based on provided [deviceProfile] - * x or y could also be -1 to indicate there is no dimension specified + * Point where x is width, and y is height of taskbar based on provided [deviceProfile] x or y + * could also be -1 to indicate there is no dimension specified */ @JvmStatic - fun getTaskbarPhoneDimensions(deviceProfile: DeviceProfile, res: Resources, - isPhoneMode: Boolean): Point { + fun getTaskbarPhoneDimensions( + deviceProfile: DeviceProfile, + res: Resources, + isPhoneMode: Boolean + ): Point { val p = Point() // Taskbar for large screen if (!isPhoneMode) { @@ -57,4 +60,4 @@ object DimensionUtils { p.y = ViewGroup.LayoutParams.MATCH_PARENT return p } -} \ No newline at end of file +} diff --git a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt index 13e56f3456..2da5eeebab 100644 --- a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt +++ b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt @@ -23,11 +23,11 @@ import androidx.test.core.app.ApplicationProvider import com.android.launcher3.DeviceProfile.DEFAULT_PROVIDER import com.android.launcher3.util.DisplayController.Info import com.android.launcher3.util.WindowBounds +import java.io.PrintWriter +import java.io.StringWriter import org.junit.Before import org.mockito.ArgumentMatchers.any import org.mockito.Mockito.mock -import java.io.PrintWriter -import java.io.StringWriter import org.mockito.Mockito.`when` as whenever abstract class DeviceProfileBaseTest { @@ -49,31 +49,36 @@ abstract class DeviceProfileBaseTest { isGestureMode = true } - protected fun newDP(): DeviceProfile = DeviceProfile( - context, - inv, - info, - windowBounds, - SparseArray(), - isMultiWindowMode, - transposeLayoutWithOrientation, - useTwoPanels, - isGestureMode, - DEFAULT_PROVIDER - ) + protected fun newDP(): DeviceProfile = + DeviceProfile( + context, + inv, + info, + windowBounds, + SparseArray(), + isMultiWindowMode, + transposeLayoutWithOrientation, + useTwoPanels, + isGestureMode, + DEFAULT_PROVIDER + ) - protected fun initializeVarsForPhone(isGestureMode: Boolean = true, - isVerticalBar: Boolean = false) { - val (x, y) = if (isVerticalBar) - Pair(2400, 1080) - else - Pair(1080, 2400) + protected fun initializeVarsForPhone( + isGestureMode: Boolean = true, + isVerticalBar: Boolean = false + ) { + val (x, y) = if (isVerticalBar) Pair(2400, 1080) else Pair(1080, 2400) - windowBounds = WindowBounds(Rect(0, 0, x, y), Rect( - if (isVerticalBar) 118 else 0, - if (isVerticalBar) 74 else 118, - if (!isGestureMode && isVerticalBar) 126 else 0, - if (isGestureMode) 63 else if (isVerticalBar) 0 else 126)) + windowBounds = + WindowBounds( + Rect(0, 0, x, y), + Rect( + if (isVerticalBar) 118 else 0, + if (isVerticalBar) 74 else 118, + if (!isGestureMode && isVerticalBar) 126 else 0, + if (isGestureMode) 63 else if (isVerticalBar) 0 else 126 + ) + ) whenever(info.isTablet(any())).thenReturn(false) whenever(info.getDensityDpi()).thenReturn(420) @@ -82,79 +87,76 @@ abstract class DeviceProfileBaseTest { this.isGestureMode = isGestureMode transposeLayoutWithOrientation = true - inv = InvariantDeviceProfile().apply { - numRows = 5 - numColumns = 4 - numSearchContainerColumns = 4 + inv = + InvariantDeviceProfile().apply { + numRows = 5 + numColumns = 4 + numSearchContainerColumns = 4 - iconSize = floatArrayOf(60f, 54f, 60f, 60f) - iconTextSize = FloatArray(4) { 14f } - deviceType = InvariantDeviceProfile.TYPE_PHONE + iconSize = floatArrayOf(60f, 54f, 60f, 60f) + iconTextSize = FloatArray(4) { 14f } + deviceType = InvariantDeviceProfile.TYPE_PHONE - minCellSize = listOf( - PointF(80f, 104f), - PointF(80f, 104f), - PointF(80f, 104f), - PointF(80f, 104f) - ).toTypedArray() + minCellSize = + listOf( + PointF(80f, 104f), + PointF(80f, 104f), + PointF(80f, 104f), + PointF(80f, 104f) + ) + .toTypedArray() - borderSpaces = listOf( - PointF(16f, 16f), - PointF(16f, 16f), - PointF(16f, 16f), - PointF(16f, 16f) - ).toTypedArray() + borderSpaces = + listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f)) + .toTypedArray() - numFolderRows = 3 - numFolderColumns = 3 - folderStyle = R.style.FolderDefaultStyle + numFolderRows = 3 + numFolderColumns = 3 + folderStyle = R.style.FolderDefaultStyle - inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_split + inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_split - horizontalMargin = FloatArray(4) { 22f } + horizontalMargin = FloatArray(4) { 22f } - allAppsCellSize = listOf( - PointF(80f, 104f), - PointF(80f, 104f), - PointF(80f, 104f), - PointF(80f, 104f) - ).toTypedArray() - allAppsIconSize = floatArrayOf(60f, 60f, 60f, 60f) - allAppsIconTextSize = FloatArray(4) { 14f } - allAppsBorderSpaces = listOf( - PointF(16f, 16f), - PointF(16f, 16f), - PointF(16f, 16f), - PointF(16f, 16f) - ).toTypedArray() + allAppsCellSize = + listOf( + PointF(80f, 104f), + PointF(80f, 104f), + PointF(80f, 104f), + PointF(80f, 104f) + ) + .toTypedArray() + allAppsIconSize = floatArrayOf(60f, 60f, 60f, 60f) + allAppsIconTextSize = FloatArray(4) { 14f } + allAppsBorderSpaces = + listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 16f)) + .toTypedArray() - numShownHotseatIcons = 4 + numShownHotseatIcons = 4 - numDatabaseHotseatIcons = 4 + numDatabaseHotseatIcons = 4 - hotseatColumnSpan = IntArray(4) { 4 } - hotseatBarBottomSpace = FloatArray(4) { 48f } - hotseatQsbSpace = FloatArray(4) { 36f } + hotseatColumnSpan = IntArray(4) { 4 } + hotseatBarBottomSpace = FloatArray(4) { 48f } + hotseatQsbSpace = FloatArray(4) { 36f } - numAllAppsColumns = 4 + numAllAppsColumns = 4 - isScalable = true + isScalable = true - inlineQsb = BooleanArray(4) { false } + inlineQsb = BooleanArray(4) { false } - devicePaddingId = R.xml.paddings_handhelds - } + devicePaddingId = R.xml.paddings_handhelds + } } - protected fun initializeVarsForTablet(isLandscape: Boolean = false, - isGestureMode: Boolean = true) { - val (x, y) = if (isLandscape) - Pair(2560, 1600) - else - Pair(1600, 2560) + protected fun initializeVarsForTablet( + isLandscape: Boolean = false, + isGestureMode: Boolean = true + ) { + val (x, y) = if (isLandscape) Pair(2560, 1600) else Pair(1600, 2560) - windowBounds = - WindowBounds(Rect(0, 0, x, y), Rect(0, 104, 0, 0)) + windowBounds = WindowBounds(Rect(0, 0, x, y), Rect(0, 104, 0, 0)) whenever(info.isTablet(any())).thenReturn(true) whenever(info.getDensityDpi()).thenReturn(320) @@ -163,85 +165,77 @@ abstract class DeviceProfileBaseTest { this.isGestureMode = isGestureMode useTwoPanels = false - inv = InvariantDeviceProfile().apply { - numRows = 5 - numColumns = 6 - numSearchContainerColumns = 3 + inv = + InvariantDeviceProfile().apply { + numRows = 5 + numColumns = 6 + numSearchContainerColumns = 3 - iconSize = FloatArray(4) { 60f } - iconTextSize = FloatArray(4) { 14f } - deviceType = InvariantDeviceProfile.TYPE_TABLET + iconSize = FloatArray(4) { 60f } + iconTextSize = FloatArray(4) { 14f } + deviceType = InvariantDeviceProfile.TYPE_TABLET - minCellSize = listOf( - PointF(102f, 120f), - PointF(120f, 104f), - PointF(102f, 120f), - PointF(102f, 120f) - ).toTypedArray() + minCellSize = + listOf( + PointF(102f, 120f), + PointF(120f, 104f), + PointF(102f, 120f), + PointF(102f, 120f) + ) + .toTypedArray() - borderSpaces = listOf( - PointF(16f, 64f), - PointF(64f, 16f), - PointF(16f, 64f), - PointF(16f, 64f) - ).toTypedArray() + borderSpaces = + listOf(PointF(16f, 64f), PointF(64f, 16f), PointF(16f, 64f), PointF(16f, 64f)) + .toTypedArray() - numFolderRows = 3 - numFolderColumns = 3 - folderStyle = R.style.FolderDefaultStyle + numFolderRows = 3 + numFolderColumns = 3 + folderStyle = R.style.FolderDefaultStyle - inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_6_5 + inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_6_5 - horizontalMargin = floatArrayOf(54f, 120f, 54f, 54f) + horizontalMargin = floatArrayOf(54f, 120f, 54f, 54f) - allAppsCellSize = listOf( - PointF(96f, 142f), - PointF(126f, 126f), - PointF(96f, 142f), - PointF(96f, 142f) - ).toTypedArray() - allAppsIconSize = FloatArray(4) { 60f } - allAppsIconTextSize = FloatArray(4) { 14f } - allAppsBorderSpaces = listOf( - PointF(8f, 16f), - PointF(16f, 16f), - PointF(8f, 16f), - PointF(8f, 16f) - ).toTypedArray() + allAppsCellSize = + listOf( + PointF(96f, 142f), + PointF(126f, 126f), + PointF(96f, 142f), + PointF(96f, 142f) + ) + .toTypedArray() + allAppsIconSize = FloatArray(4) { 60f } + allAppsIconTextSize = FloatArray(4) { 14f } + allAppsBorderSpaces = + listOf(PointF(8f, 16f), PointF(16f, 16f), PointF(8f, 16f), PointF(8f, 16f)) + .toTypedArray() - numShownHotseatIcons = 6 + numShownHotseatIcons = 6 - numDatabaseHotseatIcons = 6 + numDatabaseHotseatIcons = 6 - hotseatColumnSpan = intArrayOf(6, 4, 6, 6) - hotseatBarBottomSpace = floatArrayOf(36f, 40f, 36f, 36f) - hotseatQsbSpace = FloatArray(4) { 32f } + hotseatColumnSpan = intArrayOf(6, 4, 6, 6) + hotseatBarBottomSpace = floatArrayOf(36f, 40f, 36f, 36f) + hotseatQsbSpace = FloatArray(4) { 32f } - numAllAppsColumns = 6 + numAllAppsColumns = 6 - isScalable = true - devicePaddingId = R.xml.paddings_6x5 + isScalable = true + devicePaddingId = R.xml.paddings_6x5 - inlineQsb = booleanArrayOf( - false, - true, - false, - false - ) + inlineQsb = booleanArrayOf(false, true, false, false) - devicePaddingId = R.xml.paddings_handhelds - } + devicePaddingId = R.xml.paddings_handhelds + } } - protected fun initializeVarsForTwoPanel(isLandscape: Boolean = false, - isGestureMode: Boolean = true) { - val (x, y) = if (isLandscape) - Pair(2208, 1840) - else - Pair(1840, 2208) + protected fun initializeVarsForTwoPanel( + isLandscape: Boolean = false, + isGestureMode: Boolean = true + ) { + val (x, y) = if (isLandscape) Pair(2208, 1840) else Pair(1840, 2208) - windowBounds = WindowBounds(Rect(0, 0, x, y), - Rect(0, 110, 0, 0)) + windowBounds = WindowBounds(Rect(0, 0, x, y), Rect(0, 110, 0, 0)) whenever(info.isTablet(any())).thenReturn(true) whenever(info.getDensityDpi()).thenReturn(420) @@ -250,74 +244,63 @@ abstract class DeviceProfileBaseTest { this.isGestureMode = isGestureMode useTwoPanels = true - inv = InvariantDeviceProfile().apply { - numRows = 4 - numColumns = 4 - numSearchContainerColumns = 4 + inv = + InvariantDeviceProfile().apply { + numRows = 4 + numColumns = 4 + numSearchContainerColumns = 4 - iconSize = floatArrayOf(60f, 52f, 52f, 60f) - iconTextSize = floatArrayOf(14f, 14f, 12f, 14f) - deviceType = InvariantDeviceProfile.TYPE_MULTI_DISPLAY + iconSize = floatArrayOf(60f, 52f, 52f, 60f) + iconTextSize = floatArrayOf(14f, 14f, 12f, 14f) + deviceType = InvariantDeviceProfile.TYPE_MULTI_DISPLAY - minCellSize = listOf( - PointF(80f, 104f), - PointF(80f, 104f), - PointF(68f, 116f), - PointF(80f, 102f) - ).toTypedArray() + minCellSize = + listOf( + PointF(80f, 104f), + PointF(80f, 104f), + PointF(68f, 116f), + PointF(80f, 102f) + ) + .toTypedArray() - borderSpaces = listOf( - PointF(16f, 16f), - PointF(16f, 16f), - PointF(16f, 20f), - PointF(20f, 20f) - ).toTypedArray() + borderSpaces = + listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 20f), PointF(20f, 20f)) + .toTypedArray() - numFolderRows = 3 - numFolderColumns = 3 - folderStyle = R.style.FolderDefaultStyle + numFolderRows = 3 + numFolderColumns = 3 + folderStyle = R.style.FolderDefaultStyle - inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_split + inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_split - horizontalMargin = floatArrayOf(21.5f, 21.5f, 22.5f, 30.5f) + horizontalMargin = floatArrayOf(21.5f, 21.5f, 22.5f, 30.5f) - allAppsCellSize = listOf( - PointF(0f, 0f), - PointF(0f, 0f), - PointF(68f, 104f), - PointF(80f, 104f) - ).toTypedArray() - allAppsIconSize = floatArrayOf(60f, 60f, 52f, 60f) - allAppsIconTextSize = floatArrayOf(14f, 14f, 12f, 14f) - allAppsBorderSpaces = listOf( - PointF(16f, 16f), - PointF(16f, 16f), - PointF(16f, 28f), - PointF(20f, 16f) - ).toTypedArray() + allAppsCellSize = + listOf(PointF(0f, 0f), PointF(0f, 0f), PointF(68f, 104f), PointF(80f, 104f)) + .toTypedArray() + allAppsIconSize = floatArrayOf(60f, 60f, 52f, 60f) + allAppsIconTextSize = floatArrayOf(14f, 14f, 12f, 14f) + allAppsBorderSpaces = + listOf(PointF(16f, 16f), PointF(16f, 16f), PointF(16f, 28f), PointF(20f, 16f)) + .toTypedArray() - numShownHotseatIcons = 6 + numShownHotseatIcons = 6 - numDatabaseHotseatIcons = 6 + numDatabaseHotseatIcons = 6 - hotseatColumnSpan = IntArray(4) { 6 } - hotseatBarBottomSpace = floatArrayOf(48f, 48f, 36f, 20f) - hotseatQsbSpace = floatArrayOf(36f, 36f, 36f, 28f) + hotseatColumnSpan = IntArray(4) { 6 } + hotseatBarBottomSpace = floatArrayOf(48f, 48f, 36f, 20f) + hotseatQsbSpace = floatArrayOf(36f, 36f, 36f, 28f) - numAllAppsColumns = 6 - numDatabaseAllAppsColumns = 6 + numAllAppsColumns = 6 + numDatabaseAllAppsColumns = 6 - isScalable = true + isScalable = true - inlineQsb = booleanArrayOf( - false, - false, - false, - false - ) + inlineQsb = booleanArrayOf(false, false, false, false) - devicePaddingId = R.xml.paddings_handhelds - } + devicePaddingId = R.xml.paddings_handhelds + } } fun dump(dp: DeviceProfile): String { @@ -327,4 +310,4 @@ abstract class DeviceProfileBaseTest { printWriter.flush() return stringWriter.toString() } -} \ No newline at end of file +} diff --git a/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt b/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt index d26381dc2f..03352fe234 100644 --- a/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt +++ b/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt @@ -30,9 +30,7 @@ import com.android.launcher3.util.IntSparseArrayMap import com.android.launcher3.util.LauncherModelHelper import java.util.UUID -/** - * Base class for workspace related tests. - */ +/** Base class for workspace related tests. */ abstract class AbstractWorkspaceModelTest { companion object { val emptyScreenSpaces = listOf(Rect(0, 0, 5, 5)) @@ -64,10 +62,7 @@ abstract class AbstractWorkspaceModelTest { mModelHelper.destroy() } - - /** - * Sets up workspaces with the given screen IDs with some items and a 2x2 space. - */ + /** Sets up workspaces with the given screen IDs with some items and a 2x2 space. */ fun setupWorkspaces(screenIdsWithItems: List) { var nextItemId = 1 screenIdsWithItems.forEach { screenId -> @@ -83,8 +78,7 @@ abstract class AbstractWorkspaceModelTest { screen1: List? = null, screen2: List? = null, screen3: List? = null, - ) = listOf(screen0, screen1, screen2, screen3) - .let(this::setupWithSpaces) + ) = listOf(screen0, screen1, screen2, screen3).let(this::setupWithSpaces) private fun setupWithSpaces(workspaceSpaces: List?>) { var nextItemId = 1 @@ -110,9 +104,7 @@ abstract class AbstractWorkspaceModelTest { var itemId = itemStartId val occupancy = GridOccupancy(mIdp.numColumns, mIdp.numRows) occupancy.markCells(0, 0, mIdp.numColumns, mIdp.numRows, true) - spaces.forEach { spaceRect -> - occupancy.markCells(spaceRect, false) - } + spaces.forEach { spaceRect -> occupancy.markCells(spaceRect, false) } mExistingScreens.add(screenId) mScreenOccupancy.append(screenId, occupancy) for (x in 0 until mIdp.numColumns) { @@ -139,24 +131,21 @@ abstract class AbstractWorkspaceModelTest { return itemId } - fun getExistingItem() = WorkspaceItemInfo() - .apply { intent = Intent().setComponent(ComponentName("a", "b")) } + fun getExistingItem() = + WorkspaceItemInfo().apply { intent = Intent().setComponent(ComponentName("a", "b")) } fun getNewItem(): WorkspaceItemInfo { val itemPackage = UUID.randomUUID().toString() - return WorkspaceItemInfo() - .apply { intent = Intent().setComponent(ComponentName(itemPackage, itemPackage)) } + return WorkspaceItemInfo().apply { + intent = Intent().setComponent(ComponentName(itemPackage, itemPackage)) + } } } -data class NewItemSpace( - val screenId: Int, - val cellX: Int, - val cellY: Int -) { +data class NewItemSpace(val screenId: Int, val cellX: Int, val cellY: Int) { fun toIntArray() = intArrayOf(screenId, cellX, cellY) companion object { fun fromIntArray(array: kotlin.IntArray) = NewItemSpace(array[0], array[1], array[2]) } -} \ No newline at end of file +} diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt index 65d938b91a..749ffcf122 100644 --- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt +++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt @@ -23,9 +23,9 @@ import com.android.launcher3.model.data.ItemInfo import com.android.launcher3.model.data.WorkspaceItemInfo import com.android.launcher3.util.Executors import com.android.launcher3.util.IntArray -import com.android.launcher3.util.same -import com.android.launcher3.util.eq import com.android.launcher3.util.any +import com.android.launcher3.util.eq +import com.android.launcher3.util.same import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Before @@ -34,31 +34,24 @@ import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock -import org.mockito.MockitoAnnotations +import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.verifyZeroInteractions -import org.mockito.Mockito.times import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations -/** - * Tests for [AddWorkspaceItemsTask] - */ +/** Tests for [AddWorkspaceItemsTask] */ @SmallTest @RunWith(AndroidJUnit4::class) class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() { - @Captor - private lateinit var mAnimatedItemArgumentCaptor: ArgumentCaptor> + @Captor private lateinit var mAnimatedItemArgumentCaptor: ArgumentCaptor> - @Captor - private lateinit var mNotAnimatedItemArgumentCaptor: ArgumentCaptor> + @Captor private lateinit var mNotAnimatedItemArgumentCaptor: ArgumentCaptor> - @Mock - private lateinit var mDataModelCallbacks: BgDataModel.Callbacks - - @Mock - private lateinit var mWorkspaceItemSpaceFinder: WorkspaceItemSpaceFinder + @Mock private lateinit var mDataModelCallbacks: BgDataModel.Callbacks + @Mock private lateinit var mWorkspaceItemSpaceFinder: WorkspaceItemSpaceFinder @Before override fun setup() { @@ -89,10 +82,7 @@ class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() { @Test fun givenNewAndExistingItems_whenExecuteTask_thenOnlyAddNewItem() { - val itemsToAdd = arrayOf( - getNewItem(), - getExistingItem() - ) + val itemsToAdd = arrayOf(getNewItem(), getExistingItem()) givenNewItemSpaces(NewItemSpace(1, 0, 0)) val nonEmptyScreenIds = listOf(0) @@ -132,13 +122,14 @@ class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() { @Test fun givenMultipleItems_whenExecuteTask_thenAddThem() { - val itemsToAdd = arrayOf( - getNewItem(), - getExistingItem(), - getNewItem(), - getNewItem(), - getExistingItem(), - ) + val itemsToAdd = + arrayOf( + getNewItem(), + getExistingItem(), + getNewItem(), + getNewItem(), + getExistingItem(), + ) givenNewItemSpaces( NewItemSpace(1, 3, 3), NewItemSpace(2, 0, 0), @@ -159,27 +150,16 @@ class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() { // Items that are added to the second screen should be animated val itemsAddedToSecondScreen = addedItems.filter { it.itemInfo.screenId == 2 } assertThat(itemsAddedToSecondScreen.size).isEqualTo(2) - itemsAddedToSecondScreen.forEach { - assertThat(it.isAnimated).isTrue() - } + itemsAddedToSecondScreen.forEach { assertThat(it.isAnimated).isTrue() } verifyItemSpaceFinderCall(nonEmptyScreenIds, numberOfExpectedCall = 3) } - /** - * Sets up the item space data that will be returned from WorkspaceItemSpaceFinder. - */ + /** Sets up the item space data that will be returned from WorkspaceItemSpaceFinder. */ private fun givenNewItemSpaces(vararg newItemSpaces: NewItemSpace) { val spaceStack = newItemSpaces.toMutableList() whenever( - mWorkspaceItemSpaceFinder.findSpaceForItem( - any(), - any(), - any(), - any(), - any(), - any() + mWorkspaceItemSpaceFinder.findSpaceForItem(any(), any(), any(), any(), any(), any()) ) - ) .then { spaceStack.removeFirst().toIntArray() } } @@ -187,20 +167,21 @@ class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() { * Verifies if WorkspaceItemSpaceFinder was called with proper arguments and how many times was * it called. */ - private fun verifyItemSpaceFinderCall( - nonEmptyScreenIds: List, - numberOfExpectedCall: Int - ) { + private fun verifyItemSpaceFinderCall(nonEmptyScreenIds: List, numberOfExpectedCall: Int) { verify(mWorkspaceItemSpaceFinder, times(numberOfExpectedCall)) .findSpaceForItem( - same(mAppState), same(mModelHelper.bgDataModel), - eq(IntArray.wrap(*nonEmptyScreenIds.toIntArray())), eq(IntArray()), eq(1), eq(1) + same(mAppState), + same(mModelHelper.bgDataModel), + eq(IntArray.wrap(*nonEmptyScreenIds.toIntArray())), + eq(IntArray()), + eq(1), + eq(1) ) } /** - * Sets up the workspaces with items, executes the task, collects the added items from the - * model callback then returns it. + * Sets up the workspaces with items, executes the task, collects the added items from the model + * callback then returns it. */ private fun testAddItems( nonEmptyScreenIds: List, @@ -209,21 +190,21 @@ class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() { setupWorkspaces(nonEmptyScreenIds) val task = newTask(*itemsToAdd) var updateCount = 0 - mModelHelper.executeTaskForTest(task) - .forEach { - updateCount++ - it.run() - } + mModelHelper.executeTaskForTest(task).forEach { + updateCount++ + it.run() + } val addedItems = mutableListOf() if (updateCount > 0) { - verify(mDataModelCallbacks).bindAppsAdded( - any(), - mNotAnimatedItemArgumentCaptor.capture(), mAnimatedItemArgumentCaptor.capture() - ) + verify(mDataModelCallbacks) + .bindAppsAdded( + any(), + mNotAnimatedItemArgumentCaptor.capture(), + mAnimatedItemArgumentCaptor.capture() + ) addedItems.addAll(mAnimatedItemArgumentCaptor.value.map { AddedItem(it, true) }) addedItems.addAll(mNotAnimatedItemArgumentCaptor.value.map { AddedItem(it, false) }) - } return addedItems @@ -234,12 +215,10 @@ class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() { * with a mock. */ private fun newTask(vararg items: ItemInfo): AddWorkspaceItemsTask = - items.map { Pair.create(it, Any()) } + items + .map { Pair.create(it, Any()) } .toMutableList() .let { AddWorkspaceItemsTask(it, mWorkspaceItemSpaceFinder) } } -private data class AddedItem( - val itemInfo: ItemInfo, - val isAnimated: Boolean -) +private data class AddedItem(val itemInfo: ItemInfo, val isAnimated: Boolean) diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt index 76a186bd83..dcc8ec732a 100644 --- a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt +++ b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt @@ -17,7 +17,7 @@ package com.android.launcher3.model import android.content.Context import android.content.Intent -import android.database.Cursor; +import android.database.Cursor import android.database.sqlite.SQLiteDatabase import android.graphics.Point import android.os.Process @@ -38,7 +38,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -/** Unit tests for [GridSizeMigrationUtil] */ +/** Unit tests for [GridSizeMigrationUtil] */ @SmallTest @RunWith(AndroidJUnit4::class) class GridSizeMigrationUtilTest { @@ -64,19 +64,20 @@ class GridSizeMigrationUtilTest { context = modelHelper.sandboxContext db = modelHelper.provider.db - validPackages = setOf( - TEST_PACKAGE, - testPackage1, - testPackage2, - testPackage3, - testPackage4, - testPackage5, - testPackage6, - testPackage7, - testPackage8, - testPackage9, - testPackage10 - ) + validPackages = + setOf( + TEST_PACKAGE, + testPackage1, + testPackage2, + testPackage3, + testPackage4, + testPackage5, + testPackage6, + testPackage7, + testPackage8, + testPackage9, + testPackage10 + ) idp = InvariantDeviceProfile.INSTANCE[context] val userSerial = UserCache.INSTANCE[context].getSerialNumberForUser(Process.myUserHandle()) @@ -90,8 +91,8 @@ class GridSizeMigrationUtilTest { } /** - * Old migration logic, should be modified once [FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC] is - * not needed anymore + * Old migration logic, should be modified once [FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC] is not + * needed anymore */ @Test @Throws(Exception::class) @@ -124,25 +125,27 @@ class GridSizeMigrationUtilTest { val srcReader = DbReader(db, TMP_TABLE, context, validPackages) val destReader = DbReader(db, TABLE_NAME, context, validPackages) GridSizeMigrationUtil.migrate( - context, - db, - srcReader, - destReader, - idp.numDatabaseHotseatIcons, - Point(idp.numColumns, idp.numRows), - DeviceGridState(context), - DeviceGridState(idp) + context, + db, + srcReader, + destReader, + idp.numDatabaseHotseatIcons, + Point(idp.numColumns, idp.numRows), + DeviceGridState(context), + DeviceGridState(idp) ) // Check hotseat items - var c = context.contentResolver.query( - CONTENT_URI, - arrayOf(SCREEN, INTENT), - "container=$CONTAINER_HOTSEAT", - null, - SCREEN, - null - ) ?: throw IllegalStateException() + var c = + context.contentResolver.query( + CONTENT_URI, + arrayOf(SCREEN, INTENT), + "container=$CONTAINER_HOTSEAT", + null, + SCREEN, + null + ) + ?: throw IllegalStateException() assertThat(c.count).isEqualTo(idp.numDatabaseHotseatIcons) @@ -163,14 +166,16 @@ class GridSizeMigrationUtilTest { c.close() // Check workspace items - c = context.contentResolver.query( - CONTENT_URI, - arrayOf(CELLX, CELLY, INTENT), - "container=$CONTAINER_DESKTOP", - null, - null, - null - ) ?: throw IllegalStateException() + c = + context.contentResolver.query( + CONTENT_URI, + arrayOf(CELLX, CELLY, INTENT), + "container=$CONTAINER_DESKTOP", + null, + null, + null + ) + ?: throw IllegalStateException() intentIndex = c.getColumnIndex(INTENT) val cellXIndex = c.getColumnIndex(CELLX) @@ -195,8 +200,8 @@ class GridSizeMigrationUtilTest { } /** - * Old migration logic, should be modified once [FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC] is - * not needed anymore + * Old migration logic, should be modified once [FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC] is not + * needed anymore */ @Test @Throws(Exception::class) @@ -235,39 +240,46 @@ class GridSizeMigrationUtilTest { val readerGridB = DbReader(db, TABLE_NAME, context, validPackages) // migrate from A -> B GridSizeMigrationUtil.migrate( - context, - db, - readerGridA, - readerGridB, - idp.numDatabaseHotseatIcons, - Point(idp.numColumns, idp.numRows), - DeviceGridState(context), - DeviceGridState(idp) + context, + db, + readerGridA, + readerGridB, + idp.numDatabaseHotseatIcons, + Point(idp.numColumns, idp.numRows), + DeviceGridState(context), + DeviceGridState(idp) ) // Check hotseat items in grid B - var c = context.contentResolver.query( + var c = + context.contentResolver.query( CONTENT_URI, arrayOf(SCREEN, INTENT), "container=$CONTAINER_HOTSEAT", null, SCREEN, null - ) ?: throw IllegalStateException() + ) + ?: throw IllegalStateException() // Expected hotseat items in grid B // 2 1 3 4 - verifyHotseat(c, idp, - mutableListOf(testPackage2, testPackage1, testPackage3, testPackage4).toList()) + verifyHotseat( + c, + idp, + mutableListOf(testPackage2, testPackage1, testPackage3, testPackage4).toList() + ) // Check workspace items in grid B - c = context.contentResolver.query( + c = + context.contentResolver.query( CONTENT_URI, arrayOf(SCREEN, CELLX, CELLY, INTENT), "container=$CONTAINER_DESKTOP", null, null, null - ) ?: throw IllegalStateException() + ) + ?: throw IllegalStateException() var locMap = parseLocMap(context, c) // Expected items in grid B // _ _ _ _ @@ -285,38 +297,45 @@ class GridSizeMigrationUtilTest { // migrate from B -> A GridSizeMigrationUtil.migrate( - context, - db, - readerGridB, - readerGridA, - 5, - Point(5, 5), - DeviceGridState(idp), - DeviceGridState(context) + context, + db, + readerGridB, + readerGridA, + 5, + Point(5, 5), + DeviceGridState(idp), + DeviceGridState(context) ) // Check hotseat items in grid A - c = context.contentResolver.query( + c = + context.contentResolver.query( TMP_CONTENT_URI, arrayOf(SCREEN, INTENT), "container=$CONTAINER_HOTSEAT", null, SCREEN, null - ) ?: throw IllegalStateException() + ) + ?: throw IllegalStateException() // Expected hotseat items in grid A // 1 2 _ 3 4 - verifyHotseat(c, idp, mutableListOf( - testPackage1, testPackage2, null, testPackage3, testPackage4).toList()) + verifyHotseat( + c, + idp, + mutableListOf(testPackage1, testPackage2, null, testPackage3, testPackage4).toList() + ) // Check workspace items in grid A - c = context.contentResolver.query( + c = + context.contentResolver.query( TMP_CONTENT_URI, arrayOf(SCREEN, CELLX, CELLY, INTENT), "container=$CONTAINER_DESKTOP", null, null, null - ) ?: throw IllegalStateException() + ) + ?: throw IllegalStateException() locMap = parseLocMap(context, c) // Expected workspace items in grid A // _ _ _ _ _ @@ -338,39 +357,46 @@ class GridSizeMigrationUtilTest { // migrate from A -> B GridSizeMigrationUtil.migrate( - context, - db, - readerGridA, - readerGridB, - idp.numDatabaseHotseatIcons, - Point(idp.numColumns, idp.numRows), - DeviceGridState(context), - DeviceGridState(idp) + context, + db, + readerGridA, + readerGridB, + idp.numDatabaseHotseatIcons, + Point(idp.numColumns, idp.numRows), + DeviceGridState(context), + DeviceGridState(idp) ) // Check hotseat items in grid B - c = context.contentResolver.query( + c = + context.contentResolver.query( CONTENT_URI, arrayOf(SCREEN, INTENT), "container=$CONTAINER_HOTSEAT", null, SCREEN, null - ) ?: throw IllegalStateException() + ) + ?: throw IllegalStateException() // Expected hotseat items in grid B // 2 1 3 4 - verifyHotseat(c, idp, - mutableListOf(testPackage2, testPackage1, testPackage3, testPackage4).toList()) + verifyHotseat( + c, + idp, + mutableListOf(testPackage2, testPackage1, testPackage3, testPackage4).toList() + ) // Check workspace items in grid B - c = context.contentResolver.query( + c = + context.contentResolver.query( CONTENT_URI, arrayOf(SCREEN, CELLX, CELLY, INTENT), "container=$CONTAINER_DESKTOP", null, null, null - ) ?: throw IllegalStateException() + ) + ?: throw IllegalStateException() locMap = parseLocMap(context, c) // Expected workspace items in grid B // _ _ _ _ @@ -406,7 +432,7 @@ class GridSizeMigrationUtilTest { val locMap = mutableMapOf>() while (c.moveToNext()) { locMap[Intent.parseUri(c.getString(intentIndex), 0).getPackage()] = - Triple(c.getInt(screenIndex), c.getInt(cellXIndex), c.getInt(cellYIndex)) + Triple(c.getInt(screenIndex), c.getInt(cellXIndex), c.getInt(cellYIndex)) } c.close() return locMap.toMap() @@ -414,12 +440,13 @@ class GridSizeMigrationUtilTest { @Test fun migrateToLargerHotseat() { - val srcHotseatItems = intArrayOf( - modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI), - modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI), - modelHelper.addItem(APP_ICON, 2, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI), - modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI) - ) + val srcHotseatItems = + intArrayOf( + modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI), + modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI), + modelHelper.addItem(APP_ICON, 2, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI), + modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI) + ) val numSrcDatabaseHotseatIcons = srcHotseatItems.size idp.numDatabaseHotseatIcons = 6 idp.numColumns = 4 @@ -427,25 +454,27 @@ class GridSizeMigrationUtilTest { val srcReader = DbReader(db, TMP_TABLE, context, validPackages) val destReader = DbReader(db, TABLE_NAME, context, validPackages) GridSizeMigrationUtil.migrate( - context, - db, - srcReader, - destReader, - idp.numDatabaseHotseatIcons, - Point(idp.numColumns, idp.numRows), - DeviceGridState(context), - DeviceGridState(idp) + context, + db, + srcReader, + destReader, + idp.numDatabaseHotseatIcons, + Point(idp.numColumns, idp.numRows), + DeviceGridState(context), + DeviceGridState(idp) ) // Check hotseat items - val c = context.contentResolver.query( - CONTENT_URI, - arrayOf(SCREEN, INTENT), - "container=$CONTAINER_HOTSEAT", - null, - SCREEN, - null - ) ?: throw IllegalStateException() + val c = + context.contentResolver.query( + CONTENT_URI, + arrayOf(SCREEN, INTENT), + "container=$CONTAINER_HOTSEAT", + null, + SCREEN, + null + ) + ?: throw IllegalStateException() assertThat(c.count.toLong()).isEqualTo(numSrcDatabaseHotseatIcons.toLong()) val screenIndex = c.getColumnIndex(SCREEN) @@ -483,25 +512,27 @@ class GridSizeMigrationUtilTest { val srcReader = DbReader(db, TMP_TABLE, context, validPackages) val destReader = DbReader(db, TABLE_NAME, context, validPackages) GridSizeMigrationUtil.migrate( - context, - db, - srcReader, - destReader, - idp.numDatabaseHotseatIcons, - Point(idp.numColumns, idp.numRows), - DeviceGridState(context), - DeviceGridState(idp) + context, + db, + srcReader, + destReader, + idp.numDatabaseHotseatIcons, + Point(idp.numColumns, idp.numRows), + DeviceGridState(context), + DeviceGridState(idp) ) // Check hotseat items - val c = context.contentResolver.query( - CONTENT_URI, - arrayOf(SCREEN, INTENT), - "container=$CONTAINER_HOTSEAT", - null, - SCREEN, - null - ) ?: throw IllegalStateException() + val c = + context.contentResolver.query( + CONTENT_URI, + arrayOf(SCREEN, INTENT), + "container=$CONTAINER_HOTSEAT", + null, + SCREEN, + null + ) + ?: throw IllegalStateException() assertThat(c.count.toLong()).isEqualTo(idp.numDatabaseHotseatIcons.toLong()) val screenIndex = c.getColumnIndex(SCREEN) @@ -527,8 +558,8 @@ class GridSizeMigrationUtilTest { } /** - * Migrating from a smaller grid to a large one should keep the pages - * if the column difference is less than 2 + * Migrating from a smaller grid to a large one should keep the pages if the column difference + * is less than 2 */ @Test @Throws(Exception::class) @@ -549,25 +580,27 @@ class GridSizeMigrationUtilTest { val srcReader = DbReader(db, TMP_TABLE, context, validPackages) val destReader = DbReader(db, TABLE_NAME, context, validPackages) GridSizeMigrationUtil.migrate( - context, - db, - srcReader, - destReader, - idp.numDatabaseHotseatIcons, - Point(idp.numColumns, idp.numRows), - DeviceGridState(context), - DeviceGridState(idp) + context, + db, + srcReader, + destReader, + idp.numDatabaseHotseatIcons, + Point(idp.numColumns, idp.numRows), + DeviceGridState(context), + DeviceGridState(idp) ) // Get workspace items - val c = context.contentResolver.query( - CONTENT_URI, - arrayOf(INTENT, SCREEN), - "container=$CONTAINER_DESKTOP", - null, - null, - null - ) ?: throw IllegalStateException() + val c = + context.contentResolver.query( + CONTENT_URI, + arrayOf(INTENT, SCREEN), + "container=$CONTAINER_DESKTOP", + null, + null, + null + ) + ?: throw IllegalStateException() val intentIndex = c.getColumnIndex(INTENT) val screenIndex = c.getColumnIndex(SCREEN) @@ -589,8 +622,8 @@ class GridSizeMigrationUtilTest { } /** - * Migrating from a smaller grid to a large one should reflow the pages - * if the column difference is more than 2 + * Migrating from a smaller grid to a large one should reflow the pages if the column difference + * is more than 2 */ @Test @Throws(Exception::class) @@ -610,25 +643,27 @@ class GridSizeMigrationUtilTest { val srcReader = DbReader(db, TMP_TABLE, context, validPackages) val destReader = DbReader(db, TABLE_NAME, context, validPackages) GridSizeMigrationUtil.migrate( - context, - db, - srcReader, - destReader, - idp.numDatabaseHotseatIcons, - Point(idp.numColumns, idp.numRows), - DeviceGridState(context), - DeviceGridState(idp) + context, + db, + srcReader, + destReader, + idp.numDatabaseHotseatIcons, + Point(idp.numColumns, idp.numRows), + DeviceGridState(context), + DeviceGridState(idp) ) // Get workspace items - val c = context.contentResolver.query( - CONTENT_URI, - arrayOf(INTENT, SCREEN), - "container=$CONTAINER_DESKTOP", - null, - null, - null - ) ?: throw IllegalStateException() + val c = + context.contentResolver.query( + CONTENT_URI, + arrayOf(INTENT, SCREEN), + "container=$CONTAINER_DESKTOP", + null, + null, + null + ) + ?: throw IllegalStateException() val intentIndex = c.getColumnIndex(INTENT) val screenIndex = c.getColumnIndex(SCREEN) @@ -651,9 +686,7 @@ class GridSizeMigrationUtilTest { disableNewMigrationLogic() } - /** - * Migrating from a larger grid to a smaller, we reflow from page 0 - */ + /** Migrating from a larger grid to a smaller, we reflow from page 0 */ @Test @Throws(Exception::class) fun migrateFromLargerGrid() { @@ -672,25 +705,27 @@ class GridSizeMigrationUtilTest { val srcReader = DbReader(db, TMP_TABLE, context, validPackages) val destReader = DbReader(db, TABLE_NAME, context, validPackages) GridSizeMigrationUtil.migrate( - context, - db, - srcReader, - destReader, - idp.numDatabaseHotseatIcons, - Point(idp.numColumns, idp.numRows), - DeviceGridState(context), - DeviceGridState(idp) + context, + db, + srcReader, + destReader, + idp.numDatabaseHotseatIcons, + Point(idp.numColumns, idp.numRows), + DeviceGridState(context), + DeviceGridState(idp) ) // Get workspace items - val c = context.contentResolver.query( - CONTENT_URI, - arrayOf(INTENT, SCREEN), - "container=$CONTAINER_DESKTOP", - null, - null, - null - ) ?: throw IllegalStateException() + val c = + context.contentResolver.query( + CONTENT_URI, + arrayOf(INTENT, SCREEN), + "container=$CONTAINER_DESKTOP", + null, + null, + null + ) + ?: throw IllegalStateException() val intentIndex = c.getColumnIndex(INTENT) val screenIndex = c.getColumnIndex(SCREEN) @@ -714,11 +749,13 @@ class GridSizeMigrationUtilTest { } private fun enableNewMigrationLogic(srcGridSize: String) { - context.getSharedPreferences(FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE) + context + .getSharedPreferences(FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE) .edit() .putBoolean(FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC.key, true) .commit() - context.getSharedPreferences(LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE) + context + .getSharedPreferences(LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE) .edit() .putString(DeviceGridState.KEY_WORKSPACE_SIZE, srcGridSize) .commit() @@ -726,9 +763,10 @@ class GridSizeMigrationUtilTest { } private fun disableNewMigrationLogic() { - context.getSharedPreferences(FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE) + context + .getSharedPreferences(FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE) .edit() .putBoolean(FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC.key, false) .commit() } -} \ No newline at end of file +} diff --git a/tests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt b/tests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt index bfb1ac64c4..b3d02be460 100644 --- a/tests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt +++ b/tests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt @@ -24,9 +24,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -/** - * Tests for [WorkspaceItemSpaceFinder] - */ +/** Tests for [WorkspaceItemSpaceFinder] */ @SmallTest @RunWith(AndroidJUnit4::class) class WorkspaceItemSpaceFinderTest : AbstractWorkspaceModelTest() { @@ -44,17 +42,27 @@ class WorkspaceItemSpaceFinderTest : AbstractWorkspaceModelTest() { } private fun findSpace(spanX: Int, spanY: Int): NewItemSpace = - mItemSpaceFinder.findSpaceForItem( - mAppState, mModelHelper.bgDataModel, - mExistingScreens, mNewScreens, spanX, spanY - ) + mItemSpaceFinder + .findSpaceForItem( + mAppState, + mModelHelper.bgDataModel, + mExistingScreens, + mNewScreens, + spanX, + spanY + ) .let { NewItemSpace.fromIntArray(it) } private fun assertRegionVacant(newItemSpace: NewItemSpace, spanX: Int, spanY: Int) { assertThat( - mScreenOccupancy[newItemSpace.screenId] - .isRegionVacant(newItemSpace.cellX, newItemSpace.cellY, spanX, spanY) - ).isTrue() + mScreenOccupancy[newItemSpace.screenId].isRegionVacant( + newItemSpace.cellX, + newItemSpace.cellY, + spanX, + spanY + ) + ) + .isTrue() } @Test diff --git a/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt b/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt index c99ad7604a..951f5f86fa 100644 --- a/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt +++ b/tests/src/com/android/launcher3/nonquickstep/HotseatWidthCalculationTest.kt @@ -29,8 +29,8 @@ import org.junit.runner.RunWith class HotseatWidthCalculationTest : DeviceProfileBaseTest() { /** - * This is a case when after setting the hotseat, the space needs to be recalculated - * but it doesn't need to change QSB width or remove icons + * This is a case when after setting the hotseat, the space needs to be recalculated but it + * doesn't need to change QSB width or remove icons */ @Test fun distribute_border_space_when_space_is_enough_portrait() { @@ -51,8 +51,8 @@ class HotseatWidthCalculationTest : DeviceProfileBaseTest() { } /** - * This is a case when after setting the hotseat, and recalculating spaces - * it still needs to remove icons for everything to fit + * This is a case when after setting the hotseat, and recalculating spaces it still needs to + * remove icons for everything to fit */ @Test fun decrease_num_of_icons_when_not_enough_space_portrait() { @@ -73,8 +73,8 @@ class HotseatWidthCalculationTest : DeviceProfileBaseTest() { } /** - * This is a case when after setting the hotseat, the space needs to be recalculated - * but it doesn't need to change QSB width or remove icons + * This is a case when after setting the hotseat, the space needs to be recalculated but it + * doesn't need to change QSB width or remove icons */ @Test fun distribute_border_space_when_space_is_enough_landscape() { @@ -94,8 +94,8 @@ class HotseatWidthCalculationTest : DeviceProfileBaseTest() { } /** - * This is a case when the hotseat spans a certain amount of columns - * and the nav buttons push the hotseat to the side, but not enough to change the border space. + * This is a case when the hotseat spans a certain amount of columns and the nav buttons push + * the hotseat to the side, but not enough to change the border space. */ @Test fun nav_buttons_dont_interfere_with_required_hotseat_width() { @@ -118,9 +118,7 @@ class HotseatWidthCalculationTest : DeviceProfileBaseTest() { assertThat(dp.hotseatQsbWidth).isEqualTo(1224) } - /** - * This is a case when after setting the hotseat, the QSB width needs to be changed to fit - */ + /** This is a case when after setting the hotseat, the QSB width needs to be changed to fit */ @Test fun decrease_qsb_when_not_enough_space_landscape() { initializeVarsForTablet(isGestureMode = false, isLandscape = true) diff --git a/tests/src/com/android/launcher3/util/KotlinMockitoHelpers.kt b/tests/src/com/android/launcher3/util/KotlinMockitoHelpers.kt index 57db13ac45..4303bfb655 100644 --- a/tests/src/com/android/launcher3/util/KotlinMockitoHelpers.kt +++ b/tests/src/com/android/launcher3/util/KotlinMockitoHelpers.kt @@ -22,43 +22,41 @@ package com.android.launcher3.util * be null"). To fix this, we can use methods that modify the return type to be nullable. This * causes Kotlin to skip the null checks. */ - import org.mockito.ArgumentCaptor import org.mockito.Mockito /** - * Returns Mockito.eq() as nullable type to avoid java.lang.IllegalStateException when - * null is returned. + * Returns Mockito.eq() as nullable type to avoid java.lang.IllegalStateException when null is + * returned. * * Generic T is nullable because implicitly bounded by Any?. */ fun eq(obj: T): T = Mockito.eq(obj) /** - * Returns Mockito.same() as nullable type to avoid java.lang.IllegalStateException when - * null is returned. + * Returns Mockito.same() as nullable type to avoid java.lang.IllegalStateException when null is + * returned. * * Generic T is nullable because implicitly bounded by Any?. */ fun same(obj: T): T = Mockito.same(obj) /** - * Returns Mockito.any() as nullable type to avoid java.lang.IllegalStateException when - * null is returned. + * Returns Mockito.any() as nullable type to avoid java.lang.IllegalStateException when null is + * returned. * * Generic T is nullable because implicitly bounded by Any?. */ fun any(type: Class): T = Mockito.any(type) + inline fun any(): T = any(T::class.java) -/** - * Kotlin type-inferred version of Mockito.nullable() - */ +/** Kotlin type-inferred version of Mockito.nullable() */ inline fun nullable(): T? = Mockito.nullable(T::class.java) /** - * Returns ArgumentCaptor.capture() as nullable type to avoid java.lang.IllegalStateException - * when null is returned. + * Returns ArgumentCaptor.capture() as nullable type to avoid java.lang.IllegalStateException when + * null is returned. * * Generic T is nullable because implicitly bounded by Any?. */ @@ -82,8 +80,9 @@ inline fun mock(): T = Mockito.mock(T::class.java) /** * A kotlin implemented wrapper of [ArgumentCaptor] which prevents the following exception when * kotlin tests are mocking kotlin objects and the methods take non-null parameters: - * + * ``` * java.lang.NullPointerException: capture() must not be null + * ``` */ class KotlinArgumentCaptor constructor(clazz: Class) { private val wrapped: ArgumentCaptor = ArgumentCaptor.forClass(clazz) @@ -102,15 +101,15 @@ inline fun kotlinArgumentCaptor(): KotlinArgumentCaptor = /** * Helper function for creating and using a single-use ArgumentCaptor in kotlin. - * + * ``` * val captor = argumentCaptor() * verify(...).someMethod(captor.capture()) * val captured = captor.value - * + * ``` * becomes: - * + * ``` * val captured = withArgCaptor { verify(...).someMethod(capture()) } - * + * ``` * NOTE: this uses the KotlinArgumentCaptor to avoid the NullPointerException. */ inline fun withArgCaptor(block: KotlinArgumentCaptor.() -> Unit): T = diff --git a/tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt b/tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt index bf3a092adb..a63136e02a 100644 --- a/tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt +++ b/tests/src/com/android/launcher3/util/MultiPropertyFactoryTest.kt @@ -30,18 +30,18 @@ class MultiPropertyFactoryTest { private val received = mutableListOf() - private val receiveProperty: FloatProperty = object : FloatProperty("receive") { - override fun setValue(obj: Any?, value: Float) { - received.add(value) + private val receiveProperty: FloatProperty = + object : FloatProperty("receive") { + override fun setValue(obj: Any?, value: Float) { + received.add(value) + } + override fun get(o: Any): Float { + return 0f + } } - override fun get(o: Any): Float { - return 0f - } - } - private val factory = MultiPropertyFactory(null, receiveProperty, 3) { - x: Float, y: Float -> x + y - } + private val factory = + MultiPropertyFactory(null, receiveProperty, 3) { x: Float, y: Float -> x + y } private val p1 = factory.get(0) private val p2 = factory.get(1) diff --git a/tests/src/com/android/launcher3/util/TouchUtilTest.kt b/tests/src/com/android/launcher3/util/TouchUtilTest.kt index d6c6e91e49..235d6ec3f3 100644 --- a/tests/src/com/android/launcher3/util/TouchUtilTest.kt +++ b/tests/src/com/android/launcher3/util/TouchUtilTest.kt @@ -18,9 +18,9 @@ package com.android.launcher3.util import android.view.InputDevice import android.view.MotionEvent +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.google.common.truth.Truth.assertThat -import androidx.test.ext.junit.runners.AndroidJUnit4 import org.junit.Test import org.junit.runner.RunWith From fb4429f0b0e85d99938e0ae8165676f1c0b30e81 Mon Sep 17 00:00:00 2001 From: Luca Zuccarini Date: Tue, 10 Jan 2023 14:01:14 +0000 Subject: [PATCH 015/469] Revert^2 "Update timings for the Home <> All Apps transition." e18ac479e59d085e1db25bdba78e2e0e4aea35b3 Change-Id: Id09987b2c67be70e62349574973d4bc62b10bc9d Bug: 263221786 --- .../quickstep/util/BaseDepthController.java | 5 ++- .../AbstractStateChangeTouchController.java | 18 +++++++---- .../touch/AllAppsSwipeController.java | 31 +++++++++++-------- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/quickstep/src/com/android/quickstep/util/BaseDepthController.java b/quickstep/src/com/android/quickstep/util/BaseDepthController.java index 877e28ab4e..cecf58d105 100644 --- a/quickstep/src/com/android/quickstep/util/BaseDepthController.java +++ b/quickstep/src/com/android/quickstep/util/BaseDepthController.java @@ -108,7 +108,10 @@ public class BaseDepthController { float depth = mDepth; IBinder windowToken = mLauncher.getRootView().getWindowToken(); if (windowToken != null) { - mWallpaperManager.setWallpaperZoomOut(windowToken, depth); + // The API's full zoom-out is three times larger than the zoom-out we apply to the + // icons. To keep the two consistent throughout the animation while keeping Launcher's + // concept of full depth unchanged, we divide the depth by 3 here. + mWallpaperManager.setWallpaperZoomOut(windowToken, depth / 3); } if (!BlurUtils.supportsBlursOnWindows()) { diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java index 8f7a4ecfd1..c499e35ddd 100644 --- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java +++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java @@ -32,7 +32,6 @@ import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFram import android.animation.Animator.AnimatorListener; import android.animation.ValueAnimator; -import android.util.Log; import android.view.MotionEvent; import com.android.launcher3.Launcher; @@ -292,11 +291,18 @@ public abstract class AbstractStateChangeTouchController ? mToState : mFromState; // snap to top or bottom using the release velocity } else { - float successTransitionProgress = - mLauncher.getDeviceProfile().isTablet - && (mToState == ALL_APPS || mFromState == ALL_APPS) - ? TABLET_BOTTOM_SHEET_SUCCESS_TRANSITION_PROGRESS - : SUCCESS_TRANSITION_PROGRESS; + float successTransitionProgress = SUCCESS_TRANSITION_PROGRESS; + if (mLauncher.getDeviceProfile().isTablet + && (mToState == ALL_APPS || mFromState == ALL_APPS)) { + successTransitionProgress = TABLET_BOTTOM_SHEET_SUCCESS_TRANSITION_PROGRESS; + } else if (!mLauncher.getDeviceProfile().isTablet + && mToState == ALL_APPS && mFromState == NORMAL) { + successTransitionProgress = AllAppsSwipeController.ALL_APPS_STATE_TRANSITION_MANUAL; + } else if (!mLauncher.getDeviceProfile().isTablet + && mToState == NORMAL && mFromState == ALL_APPS) { + successTransitionProgress = + 1 - AllAppsSwipeController.ALL_APPS_STATE_TRANSITION_MANUAL; + } targetState = (interpolatedProgress > successTransitionProgress) ? mToState : mFromState; } diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java index 5279dec73b..bfd0e1b74b 100644 --- a/src/com/android/launcher3/touch/AllAppsSwipeController.java +++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java @@ -17,7 +17,6 @@ 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; @@ -64,11 +63,14 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController { // ---- Custom interpolators for NORMAL -> ALL_APPS on phones only. ---- - private static final float WORKSPACE_MOTION_START_ATOMIC = 0.1667f; - private static final float ALL_APPS_STATE_TRANSITION_ATOMIC = 0.305f; - private static final float ALL_APPS_STATE_TRANSITION_MANUAL = 0.4f; - private static final float ALL_APPS_FADE_END_ATOMIC = 0.4717f; + public static final float ALL_APPS_STATE_TRANSITION_ATOMIC = 0.3333f; + public static final float ALL_APPS_STATE_TRANSITION_MANUAL = 0.4f; + private static final float ALL_APPS_FADE_END_ATOMIC = 0.8333f; + private static final float ALL_APPS_FADE_END_MANUAL = 0.8f; private static final float ALL_APPS_FULL_DEPTH_PROGRESS = 0.5f; + private static final float SCRIM_FADE_START_ATOMIC = 0.2642f; + private static final float SCRIM_FADE_START_MANUAL = 0.117f; + private static final float WORKSPACE_MOTION_START_ATOMIC = 0.1667f; private static final Interpolator LINEAR_EARLY_MANUAL = Interpolators.clampToProgress(LINEAR, 0f, ALL_APPS_STATE_TRANSITION_MANUAL); @@ -98,27 +100,30 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController { public static final Interpolator HOTSEAT_FADE_ATOMIC = STEP_TRANSITION_ATOMIC; public static final Interpolator HOTSEAT_FADE_MANUAL = STEP_TRANSITION_MANUAL; - public static final Interpolator HOTSEAT_SCALE_ATOMIC = STEP_TRANSITION_ATOMIC; - public static final Interpolator HOTSEAT_SCALE_MANUAL = LINEAR_EARLY_MANUAL; - - public static final Interpolator HOTSEAT_TRANSLATE_ATOMIC = + public static final Interpolator HOTSEAT_SCALE_ATOMIC = Interpolators.clampToProgress( EMPHASIZED_ACCELERATE, WORKSPACE_MOTION_START_ATOMIC, ALL_APPS_STATE_TRANSITION_ATOMIC); + public static final Interpolator HOTSEAT_SCALE_MANUAL = LINEAR_EARLY_MANUAL; + + public static final Interpolator HOTSEAT_TRANSLATE_ATOMIC = STEP_TRANSITION_ATOMIC; public static final Interpolator HOTSEAT_TRANSLATE_MANUAL = STEP_TRANSITION_MANUAL; public static final Interpolator SCRIM_FADE_ATOMIC = Interpolators.clampToProgress( Interpolators.mapToProgress(LINEAR, 0f, 0.8f), - WORKSPACE_MOTION_START_ATOMIC, ALL_APPS_STATE_TRANSITION_ATOMIC); - public static final Interpolator SCRIM_FADE_MANUAL = LINEAR_EARLY_MANUAL; + SCRIM_FADE_START_ATOMIC, ALL_APPS_STATE_TRANSITION_ATOMIC); + public static final Interpolator SCRIM_FADE_MANUAL = + Interpolators.clampToProgress( + LINEAR, SCRIM_FADE_START_MANUAL, ALL_APPS_STATE_TRANSITION_MANUAL); public static final Interpolator ALL_APPS_FADE_ATOMIC = Interpolators.clampToProgress( - Interpolators.mapToProgress(DECELERATED_EASE, 0.2f, 1f), + Interpolators.mapToProgress(EMPHASIZED_DECELERATE, 0.2f, 1f), ALL_APPS_STATE_TRANSITION_ATOMIC, ALL_APPS_FADE_END_ATOMIC); public static final Interpolator ALL_APPS_FADE_MANUAL = - Interpolators.clampToProgress(LINEAR, ALL_APPS_STATE_TRANSITION_MANUAL, 1f); + Interpolators.clampToProgress( + LINEAR, ALL_APPS_STATE_TRANSITION_MANUAL, ALL_APPS_FADE_END_MANUAL); public static final Interpolator ALL_APPS_VERTICAL_PROGRESS_ATOMIC = Interpolators.clampToProgress( From 06f36e8568e436087373027e68f305e9cc53b283 Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Fri, 6 Jan 2023 10:50:54 +0000 Subject: [PATCH 016/469] Reduce icon/text simultaneously when cellHeight is not enough for largest display size - Follow-up of http://ag/20691430 - Reduce iconDrawablePaddingPx to further recover space - Then proportional reduce both iconSizePx and iconTextSizePx - This will result in larger icon/text size in px when going from larger to largest for most devices - For extremely large desnity (> largest for most devices), iconSizePx/iconTextSizePx will inevitably reduce due to smaller availalbe space Fix: 264149604 Test: test different density between larger and largest, check that icon/text size continues to grow in px Change-Id: I1633566b9372acf1393c7e997598cb3e46c4a507 --- src/com/android/launcher3/DeviceProfile.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 25520e1bea..f124940e3d 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -910,12 +910,24 @@ public class DeviceProfile { cellHeightPx = cellContentHeight; cellLayoutBorderSpacePx.y -= extraHeightRequired / numBorders; } else { - // If it still doesn't fit, set borderSpace to 0 and distribute the space for - // cellHeight, and reduce iconSize. + // If it still doesn't fit, set borderSpace to 0 to recover space. cellHeightPx = (cellHeightPx * inv.numRows + cellLayoutBorderSpacePx.y * numBorders) / inv.numRows; - iconSizePx = Math.min(iconSizePx, cellHeightPx - cellTextAndPaddingHeight); cellLayoutBorderSpacePx.y = 0; + // Reduce iconDrawablePaddingPx to make cellContentHeight smaller. + int cellContentWithoutPadding = cellContentHeight - iconDrawablePaddingPx; + if (cellContentWithoutPadding <= cellHeightPx) { + iconDrawablePaddingPx = cellContentHeight - cellHeightPx; + } else { + // If it still doesn't fit, set iconDrawablePaddingPx to 0 to recover space, + // then proportional reduce iconSizePx and iconTextSizePx to fit. + iconDrawablePaddingPx = 0; + float ratio = cellHeightPx / (float) cellContentWithoutPadding; + iconSizePx = (int) (iconSizePx * ratio); + iconTextSizePx = (int) (iconTextSizePx * ratio); + } + cellTextAndPaddingHeight = + iconDrawablePaddingPx + Utilities.calculateTextHeight(iconTextSizePx); } cellContentHeight = iconSizePx + cellTextAndPaddingHeight; } From 5fe99d560e40dbbe32182885eac154281d27a646 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 5 Jan 2023 11:19:52 -0800 Subject: [PATCH 017/469] Fetching the session info on the background thread when launching pening icon Bug: 242669604 Test: Verified that the RPC is not made on UI thread Change-Id: I4299628b8b3e9aa2844f5556c57f47d997dd85e1 --- .../launcher3/touch/ItemClickHandler.java | 64 ++++++++++--------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java index b7e01057b9..64951ca4d3 100644 --- a/src/com/android/launcher3/touch/ItemClickHandler.java +++ b/src/com/android/launcher3/touch/ItemClickHandler.java @@ -23,14 +23,14 @@ import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_LO import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import android.app.AlertDialog; import android.content.Context; import android.content.Intent; import android.content.pm.LauncherApps; import android.content.pm.PackageInstaller.SessionInfo; -import android.os.Process; -import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import android.view.View; @@ -66,6 +66,8 @@ import com.android.launcher3.widget.WidgetAddFlowHandler; import com.android.launcher3.widget.WidgetManagerHelper; import java.util.Collections; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; /** * Class for handling clicks on workspace and all-apps items @@ -156,32 +158,18 @@ public class ItemClickHandler { private static void onClickPendingAppItem(View v, Launcher launcher, String packageName, boolean downloadStarted) { - if (downloadStarted) { - // If the download has started, simply direct to the market app. - startMarketIntentForPackage(v, launcher, packageName); - return; - } - UserHandle user = v.getTag() instanceof ItemInfo - ? ((ItemInfo) v.getTag()).user : Process.myUserHandle(); - new AlertDialog.Builder(launcher) - .setTitle(R.string.abandoned_promises_title) - .setMessage(R.string.abandoned_promise_explanation) - .setPositiveButton(R.string.abandoned_search, - (d, i) -> startMarketIntentForPackage(v, launcher, packageName)) - .setNeutralButton(R.string.abandoned_clean_this, - (d, i) -> launcher.getWorkspace() - .persistRemoveItemsByMatcher(ItemInfoMatcher.ofPackages( - Collections.singleton(packageName), user), - "user explicitly removes the promise app icon")) - .create().show(); - } - - private static void startMarketIntentForPackage(View v, Launcher launcher, String packageName) { ItemInfo item = (ItemInfo) v.getTag(); + CompletableFuture siFuture; if (Utilities.ATLEAST_Q) { - SessionInfo sessionInfo = InstallSessionHelper.INSTANCE.get(launcher) - .getActiveSessionInfo(item.user, packageName); - if (sessionInfo != null) { + siFuture = CompletableFuture.supplyAsync(() -> + InstallSessionHelper.INSTANCE.get(launcher) + .getActiveSessionInfo(item.user, packageName), + UI_HELPER_EXECUTOR); + } else { + siFuture = CompletableFuture.completedFuture(null); + } + Consumer marketLaunchAction = sessionInfo -> { + if (sessionInfo != null && Utilities.ATLEAST_Q) { LauncherApps launcherApps = launcher.getSystemService(LauncherApps.class); try { launcherApps.startPackageInstallerSessionDetailsActivity(sessionInfo, null, @@ -191,11 +179,27 @@ public class ItemClickHandler { Log.e(TAG, "Unable to launch market intent for package=" + packageName, e); } } - } + // Fallback to using custom market intent. + Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName); + launcher.startActivitySafely(v, intent, item); + }; - // Fallback to using custom market intent. - Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName); - launcher.startActivitySafely(v, intent, item); + if (downloadStarted) { + // If the download has started, simply direct to the market app. + siFuture.thenAcceptAsync(marketLaunchAction, MAIN_EXECUTOR); + return; + } + new AlertDialog.Builder(launcher) + .setTitle(R.string.abandoned_promises_title) + .setMessage(R.string.abandoned_promise_explanation) + .setPositiveButton(R.string.abandoned_search, + (d, i) -> siFuture.thenAcceptAsync(marketLaunchAction, MAIN_EXECUTOR)) + .setNeutralButton(R.string.abandoned_clean_this, + (d, i) -> launcher.getWorkspace() + .persistRemoveItemsByMatcher(ItemInfoMatcher.ofPackages( + Collections.singleton(packageName), item.user), + "user explicitly removes the promise app icon")) + .create().show(); } /** From 774f05226b01080d9c49b71b1685c18fd9161302 Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Thu, 5 Jan 2023 16:29:40 -0800 Subject: [PATCH 018/469] Hide DesktopTaskView background while animating While animating DesktopTaskView to/from fullscreen, hide the background. Background should only show up when this taskview is shown in recents. While it is animating, we don't need to show it. Bug: 263264985 Test: manual, trigger transient taskbar, observe that background does not flicker Change-Id: I76d0eb1857645b7f04a0b2e1eebe2393c252c257 --- .../android/quickstep/views/DesktopTaskView.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java index c878278da5..858f6abbc1 100644 --- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java +++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java @@ -81,6 +81,8 @@ public class DesktopTaskView extends TaskView { private final ArrayList> mPendingThumbnailRequests = new ArrayList<>(); + private ShapeDrawable mBackground; + public DesktopTaskView(Context context) { this(context, null); } @@ -99,10 +101,11 @@ public class DesktopTaskView extends TaskView { float[] outerRadii = new float[8]; Arrays.fill(outerRadii, getTaskCornerRadius()); RoundRectShape shape = new RoundRectShape(outerRadii, null, null); - ShapeDrawable background = new ShapeDrawable(shape); - background.setTint(getResources().getColor(android.R.color.system_neutral2_300)); + mBackground = new ShapeDrawable(shape); + mBackground.setTint(getResources().getColor(android.R.color.system_neutral2_300, + getContext().getTheme())); // TODO(b/244348395): this should be wallpaper - setBackground(background); + setBackground(mBackground); mSnapshotViews.add(mSnapshotView); } @@ -427,6 +430,12 @@ public class DesktopTaskView extends TaskView { // TODO(b/249371338): this copies parent implementation and makes it work for N thumbs progress = Utilities.boundToRange(progress, 0, 1); mFullscreenProgress = progress; + if (mFullscreenProgress > 0) { + // Don't show background while we are transitioning to/from fullscreen + setBackground(null); + } else { + setBackground(mBackground); + } for (int i = 0; i < mSnapshotViewMap.size(); i++) { TaskThumbnailView thumbnailView = mSnapshotViewMap.valueAt(i); thumbnailView.getTaskOverlay().setFullscreenProgress(progress); From ef35e84d1940cc9fcf9f17cf0df2fc138986f19e Mon Sep 17 00:00:00 2001 From: Jagrut Desai Date: Tue, 3 Jan 2023 15:50:54 -0500 Subject: [PATCH 019/469] Checking can start system gesture before creation of TaskbarStashInputConsumer, so that taskbar does not show up when user performs swipe up from lock screen or notification panel. Test: visual(video in buganizer) Fix:261524189, 262589756 Change-Id: I5252281025953753f9184f1b19c534513b5800fa --- .../src/com/android/quickstep/TouchInteractionService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 5d17cc77f4..08a0ab3fb8 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -800,7 +800,7 @@ public class TouchInteractionService extends Service // If Taskbar is present, we listen for long press to unstash it. TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext(); - if (tac != null) { + if (tac != null && canStartSystemGesture) { reasonString.append(NEWLINE_PREFIX) .append(reasonPrefix) .append(SUBSTRING_PREFIX) From 5f8dd2023635b26a65052ff6ead41938f48f43db Mon Sep 17 00:00:00 2001 From: Jeremy Sim Date: Thu, 29 Dec 2022 16:18:22 -0800 Subject: [PATCH 020/469] Fix transition animations for launch-from-staged operation This change makes it so that a smoother animation will play when the user cancels the splitscreen flow by tapping the staged app to launch the app in fullscreen. Since this was the only blocker to activating the feature, this change also enables launch-from-staged by default and removes the flag. Fixes: 257513449 Test: Manual Change-Id: Iabff2fc72bfcd6975d216b5cd86e0df7a772b308 --- .../com/android/quickstep/views/RecentsView.java | 16 ++++------------ .../android/launcher3/config/FeatureFlags.java | 2 +- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 63aea452d0..85b222140a 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -2966,7 +2966,7 @@ public abstract class RecentsView launchStagedTask()); + pendingAnimation.addEndListener(animationSuccess -> + mSplitSelectStateController.launchSplitTasks(launchSuccess -> + resetFromSplitSelectionState())); pendingAnimation.buildAnim().start(); } @@ -4744,16 +4746,6 @@ public abstract class RecentsView resetFromSplitSelectionState()); - } else { - // Split staging was started from a new intent (from app menu in Home/AllApps) - mActivity.startActivity(mSplitSelectSource.intent); - } - } - protected void onTaskLaunchAnimationEnd(boolean success) { if (success) { resetTaskVisuals(); diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index b46e43f63b..3a11a325f9 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -364,7 +364,7 @@ public final class FeatureFlags { "ENABLE_DEVICE_PROFILE_LOGGING", false, "Allows DeviceProfile logging"); public static final BooleanFlag ENABLE_LAUNCH_FROM_STAGED_APP = getDebugFlag( - "ENABLE_LAUNCH_FROM_STAGED_APP", false, + "ENABLE_LAUNCH_FROM_STAGED_APP", true, "Enable the ability to tap a staged app during split select to launch it in full screen" ); From 50f1e8dfc2fd115c3c0e1b7602a3e18a4f516f8e Mon Sep 17 00:00:00 2001 From: Jagrut Desai Date: Mon, 9 Jan 2023 11:17:53 -0600 Subject: [PATCH 021/469] Adding margin height for transient taskbar to allow users to stash taskbar from full height. Test: visual(video in buganizer) Fix: 263526574 Change-Id: I21fd1273b83a4e5efdf7aeacae1303513b9b4d97 --- .../android/launcher3/taskbar/TaskbarStashController.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index c269648f7d..6031b49874 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -364,7 +364,12 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba * Returns the height that taskbar will be touchable. */ public int getTouchableHeight() { - return mIsStashed ? mStashedHeight : mUnstashedHeight; + int bottomMargin = 0; + if (DisplayController.isTransientTaskbar(mActivity)) { + bottomMargin = mActivity.getResources().getDimensionPixelSize( + R.dimen.transient_taskbar_margin); + } + return mIsStashed ? mStashedHeight : (mUnstashedHeight + bottomMargin); } /** From 50ef8004e0a94f35a57e06f7ebf6daccc6200fcc Mon Sep 17 00:00:00 2001 From: Luca Zuccarini Date: Tue, 3 Jan 2023 14:19:19 +0000 Subject: [PATCH 022/469] [Toast] Add a feature flag for result launch animations. Bug: 250588519 Test: manual Change-Id: I6d605bf8b1008a3b12497e1d04601735ad79c001 --- src/com/android/launcher3/config/FeatureFlags.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 082f6a1a95..e12110c292 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -273,6 +273,10 @@ public final class FeatureFlags { "ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES", false, "Enable option to replace decorator-based search result backgrounds with drawables"); + public static final BooleanFlag ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION = new DeviceFlag( + "ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION", false, + "Enable option to launch search results using the new standardized transitions"); + public static final BooleanFlag TWO_PREDICTED_ROWS_ALL_APPS_SEARCH = new DeviceFlag( "TWO_PREDICTED_ROWS_ALL_APPS_SEARCH", false, "Use 2 rows of app predictions in All Apps search zero-state"); From 7351218fab3b830de88c115412522f4de0739d73 Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Tue, 3 Jan 2023 17:03:04 +0000 Subject: [PATCH 023/469] Update overview actions top margin Bug: 260596114 Test: manual Change-Id: Ic5f51a8d0505571272e5ad03bd16db2b903fd697 --- quickstep/res/values-sw600dp-land/dimens.xml | 3 --- quickstep/res/values-sw600dp/dimens.xml | 2 ++ quickstep/res/values-sw720dp-land/dimens.xml | 20 -------------------- 3 files changed, 2 insertions(+), 23 deletions(-) delete mode 100644 quickstep/res/values-sw720dp-land/dimens.xml diff --git a/quickstep/res/values-sw600dp-land/dimens.xml b/quickstep/res/values-sw600dp-land/dimens.xml index dc10c24745..4ee388a8f6 100644 --- a/quickstep/res/values-sw600dp-land/dimens.xml +++ b/quickstep/res/values-sw600dp-land/dimens.xml @@ -15,9 +15,6 @@ */ --> - - 12dp - 48dp diff --git a/quickstep/res/values-sw600dp/dimens.xml b/quickstep/res/values-sw600dp/dimens.xml index 5899814600..daf1f63dc1 100644 --- a/quickstep/res/values-sw600dp/dimens.xml +++ b/quickstep/res/values-sw600dp/dimens.xml @@ -33,6 +33,8 @@ 36dp 64dp + + 24dp 120dp diff --git a/quickstep/res/values-sw720dp-land/dimens.xml b/quickstep/res/values-sw720dp-land/dimens.xml deleted file mode 100644 index 02d11892dd..0000000000 --- a/quickstep/res/values-sw720dp-land/dimens.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - 20dp - From aaa77b8035a983eca5ea874e95c80a2bc64aba90 Mon Sep 17 00:00:00 2001 From: Miranda Kephart Date: Fri, 6 Jan 2023 11:48:56 -0500 Subject: [PATCH 024/469] Update ImageActionUtils to construct a ScreenshotRequest Bug: 264457397 Test: atest Merged-In: Ib629f5d9eed2091f2bc4c1082d5e0dd3d2f9176b Change-Id: Ib629f5d9eed2091f2bc4c1082d5e0dd3d2f9176b --- .../com/android/quickstep/SystemUiProxy.java | 11 ++++------ .../quickstep/util/ImageActionUtils.java | 20 +++++++++++++------ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index bb973342d5..00fb7ec299 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -29,7 +29,6 @@ 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; import android.os.Handler; @@ -51,10 +50,10 @@ import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; import com.android.internal.logging.InstanceId; +import com.android.internal.util.ScreenshotRequest; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.SplitConfigurationOptions; import com.android.systemui.shared.recents.ISystemUiProxy; -import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController; import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController; import com.android.systemui.shared.system.smartspace.SmartspaceState; @@ -384,14 +383,12 @@ public class SystemUiProxy implements ISystemUiProxy { } @Override - public void handleImageBundleAsScreenshot(Bundle screenImageBundle, Rect locationInScreen, - Insets visibleInsets, Task.TaskKey task) { + public void takeScreenshot(ScreenshotRequest request) { if (mSystemUiProxy != null) { try { - mSystemUiProxy.handleImageBundleAsScreenshot(screenImageBundle, locationInScreen, - visibleInsets, task); + mSystemUiProxy.takeScreenshot(request); } catch (RemoteException e) { - Log.w(TAG, "Failed call handleImageBundleAsScreenshot"); + Log.w(TAG, "Failed call takeScreenshot"); } } } diff --git a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java index 9fe24dec66..3a1c99b078 100644 --- a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java +++ b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java @@ -18,6 +18,8 @@ package com.android.quickstep.util; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION; +import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW; +import static android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR; @@ -47,7 +49,7 @@ import androidx.annotation.WorkerThread; import androidx.core.content.FileProvider; import com.android.internal.app.ChooserActivity; -import com.android.internal.util.ScreenshotHelper; +import com.android.internal.util.ScreenshotRequest; import com.android.launcher3.BuildConfig; import com.android.quickstep.SystemUiProxy; import com.android.systemui.shared.recents.model.Task; @@ -74,11 +76,17 @@ public class ImageActionUtils { * Saves screenshot to location determine by SystemUiProxy */ public static void saveScreenshot(SystemUiProxy systemUiProxy, Bitmap screenshot, - Rect screenshotBounds, - Insets visibleInsets, Task.TaskKey task) { - systemUiProxy.handleImageBundleAsScreenshot( - ScreenshotHelper.HardwareBitmapBundler.hardwareBitmapToBundle(screenshot), - screenshotBounds, visibleInsets, task); + Rect screenshotBounds, Insets visibleInsets, Task.TaskKey task) { + ScreenshotRequest request = + new ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OVERVIEW) + .setTopComponent(task.sourceComponent) + .setTaskId(task.id) + .setUserId(task.userId) + .setBitmap(screenshot) + .setBoundsOnScreen(screenshotBounds) + .setInsets(visibleInsets) + .build(); + systemUiProxy.takeScreenshot(request); } /** From f54e016c82aef00661b59ac67ebcdb398f5b63a1 Mon Sep 17 00:00:00 2001 From: Brandon Dayauon Date: Tue, 10 Jan 2023 10:50:55 -0800 Subject: [PATCH 025/469] split the scrolling logging into work,personal, and search - moved collapse/extend logging with the other scroll logging bug: 263817965 test: Manual - WW Change-Id: I9cd57b895c0c2edb7fe39ef98dced574d09a0953 --- .../allapps/AllAppsRecyclerView.java | 44 ++++++++++++++----- .../launcher3/allapps/WorkModeSwitch.java | 2 - .../launcher3/logging/StatsLogManager.java | 23 ++++++---- 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index d308fcb3b8..8cb31fa2e1 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -17,11 +17,15 @@ package com.android.launcher3.allapps; import static com.android.launcher3.logger.LauncherAtom.ContainerInfo; import static com.android.launcher3.logger.LauncherAtom.SearchResultContainer; -import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_DOWN; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_PERSONAL_SCROLLED_DOWN; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_PERSONAL_SCROLLED_UP; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION; -import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_UP; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SEARCH_SCROLLED_DOWN; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SEARCH_SCROLLED_UP; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_FAB_BUTTON_COLLAPSE; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_FAB_BUTTON_EXTEND; import static com.android.launcher3.util.LogConfig.SEARCH_LOGGING; import android.content.Context; @@ -275,17 +279,37 @@ public class AllAppsRecyclerView extends FastScrollRecyclerView { private void logCumulativeVerticalScroll() { ActivityContext context = ActivityContext.lookupContext(getContext()); StatsLogManager mgr = context.getStatsLogManager(); - ExtendedEditText editText = context.getAppsView().getSearchUiManager().getEditText(); + ActivityAllAppsContainerView appsView = context.getAppsView(); + ExtendedEditText editText = appsView.getSearchUiManager().getEditText(); ContainerInfo containerInfo = ContainerInfo.newBuilder().setSearchResultContainer( SearchResultContainer .newBuilder() .setQueryLength((editText == null) ? -1 : editText.length())).build(); - - // mCumulativeVerticalScroll == 0 when user comes back to original position, we don't - // know the direction of scrolling. - mgr.logger().withContainerInfo(containerInfo).log( - mCumulativeVerticalScroll == 0 ? LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION - : (mCumulativeVerticalScroll > 0) ? LAUNCHER_ALLAPPS_SCROLLED_DOWN - : LAUNCHER_ALLAPPS_SCROLLED_UP); + if (mCumulativeVerticalScroll == 0) { + // mCumulativeVerticalScroll == 0 when user comes back to original position, we + // don't know the direction of scrolling. + mgr.logger().withContainerInfo(containerInfo).log( + LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION); + return; + } else if (appsView.isSearching()) { + // In search results page + mgr.logger().withContainerInfo(containerInfo).log((mCumulativeVerticalScroll > 0) + ? LAUNCHER_ALLAPPS_SEARCH_SCROLLED_DOWN + : LAUNCHER_ALLAPPS_SEARCH_SCROLLED_UP); + return; + } else if (appsView.mViewPager != null) { + int currentPage = appsView.mViewPager.getCurrentPage(); + if (currentPage == BaseAllAppsContainerView.AdapterHolder.WORK) { + // In work A-Z list + mgr.logger().withContainerInfo(containerInfo).log((mCumulativeVerticalScroll > 0) + ? LAUNCHER_WORK_FAB_BUTTON_COLLAPSE + : LAUNCHER_WORK_FAB_BUTTON_EXTEND); + return; + } + } + // In personal A-Z list + mgr.logger().withContainerInfo(containerInfo).log((mCumulativeVerticalScroll > 0) + ? LAUNCHER_ALLAPPS_PERSONAL_SCROLLED_DOWN + : LAUNCHER_ALLAPPS_PERSONAL_SCROLLED_UP); } } diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java index 11ce738208..3f2f21d470 100644 --- a/src/com/android/launcher3/allapps/WorkModeSwitch.java +++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java @@ -203,12 +203,10 @@ public class WorkModeSwitch extends LinearLayout implements Insettable, public void extend() { mTextView.setVisibility(VISIBLE); - mStatsLogManager.logger().log(LAUNCHER_WORK_FAB_BUTTON_EXTEND); } public void shrink(){ mTextView.setVisibility(GONE); - mStatsLogManager.logger().log(LAUNCHER_WORK_FAB_BUTTON_COLLAPSE); } public int getScrollThreshold() { diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java index 2159c6b68f..5f6df277f8 100644 --- a/src/com/android/launcher3/logging/StatsLogManager.java +++ b/src/com/android/launcher3/logging/StatsLogManager.java @@ -558,14 +558,11 @@ public class StatsLogManager implements ResourceBasedOverride { + "result page etc.") LAUNCHER_ALLAPPS_SCROLLED(985), - @UiEvent(doc = "User scrolled up on one of the all apps surfaces such as A-Z list, search " - + "result page etc.") - LAUNCHER_ALLAPPS_SCROLLED_UP(1229), + @UiEvent(doc = "User scrolled up on the all apps personal A-Z list.") + LAUNCHER_ALLAPPS_PERSONAL_SCROLLED_UP(1287), - @UiEvent(doc = - "User scrolled down on one of the all apps surfaces such as A-Z list, search " - + "result page etc.") - LAUNCHER_ALLAPPS_SCROLLED_DOWN(1230), + @UiEvent(doc = "User scrolled down on the all apps personal A-Z list.") + LAUNCHER_ALLAPPS_PERSONAL_SCROLLED_DOWN(1288), @UiEvent(doc = "User scrolled on one of the all apps surfaces such as A-Z list, search " + "result page etc and we don't know the direction since user came back to " @@ -626,11 +623,19 @@ public class StatsLogManager implements ResourceBasedOverride { @UiEvent(doc = "User has invoked split to left half with a keyboard shortcut.") LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_LEFT_TOP(1233), - @UiEvent(doc = "User has collapsed the work FAB button by swiping down") + @UiEvent(doc = "User has collapsed the work FAB button by scrolling down in the all apps" + + " work A-Z list.") LAUNCHER_WORK_FAB_BUTTON_COLLAPSE(1276), - @UiEvent(doc = "User has collapsed the work FAB button by swiping up") + @UiEvent(doc = "User has collapsed the work FAB button by scrolling up in the all apps" + + " work A-Z list.") LAUNCHER_WORK_FAB_BUTTON_EXTEND(1277), + + @UiEvent(doc = "User scrolled down on the search result page.") + LAUNCHER_ALLAPPS_SEARCH_SCROLLED_DOWN(1285), + + @UiEvent(doc = "User scrolled up on the search result page.") + LAUNCHER_ALLAPPS_SEARCH_SCROLLED_UP(1286), ; // ADD MORE From 2a58ddb89ffb0e9a69ae4385ebf6fcd13bc42e52 Mon Sep 17 00:00:00 2001 From: Stefan Andonian Date: Thu, 5 Jan 2023 18:31:06 +0000 Subject: [PATCH 026/469] Refactor the OnUserUnlock code out of RecentsAnimationDeviceState and into its own class. Bug: 251502424 Test: Compilation threw no errors and user unlock behavior worked correctly. Change-Id: Ifa42dc32f90dfa4fda8df0e52811ddfe20cc5a9b --- .../RecentsAnimationDeviceState.java | 51 +---------- .../quickstep/TouchInteractionService.java | 26 +++--- .../android/launcher3/util/LockedUserState.kt | 57 ++++++++++++ .../launcher3/util/LockedUserStateTest.kt | 88 +++++++++++++++++++ 4 files changed, 161 insertions(+), 61 deletions(-) create mode 100644 src/com/android/launcher3/util/LockedUserState.kt create mode 100644 tests/src/com/android/launcher3/util/LockedUserStateTest.kt diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index 9e25555eec..f1c0f3e210 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -17,7 +17,6 @@ package com.android.quickstep; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static android.content.Intent.ACTION_USER_UNLOCKED; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.launcher3.util.DisplayController.CHANGE_ALL; @@ -50,10 +49,8 @@ import android.content.Context; import android.graphics.Region; import android.inputmethodservice.InputMethodService; import android.net.Uri; -import android.os.Process; import android.os.RemoteException; import android.os.SystemProperties; -import android.os.UserManager; import android.provider.Settings; import android.view.MotionEvent; @@ -63,9 +60,9 @@ import androidx.annotation.NonNull; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener; import com.android.launcher3.util.DisplayController.Info; +import com.android.launcher3.util.LockedUserState; import com.android.launcher3.util.NavigationMode; import com.android.launcher3.util.SettingsCache; -import com.android.launcher3.util.SimpleBroadcastReceiver; import com.android.quickstep.TopTaskTracker.CachedTaskInfo; import com.android.quickstep.util.NavBarPosition; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -109,15 +106,6 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener { private final boolean mIsOneHandedModeSupported; private boolean mPipIsActive; - private boolean mIsUserUnlocked; - private final ArrayList mUserUnlockedActions = new ArrayList<>(); - private final SimpleBroadcastReceiver mUserUnlockedReceiver = new SimpleBroadcastReceiver(i -> { - if (ACTION_USER_UNLOCKED.equals(i.getAction())) { - mIsUserUnlocked = true; - notifyUserUnlocked(); - } - }); - private int mGestureBlockingTaskId = -1; private @NonNull Region mExclusionRegion = new Region(); private SystemGestureExclusionListenerCompat mExclusionListener; @@ -143,14 +131,6 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener { runOnDestroy(mRotationTouchHelper::destroy); } - // Register for user unlocked if necessary - mIsUserUnlocked = context.getSystemService(UserManager.class) - .isUserUnlocked(Process.myUserHandle()); - if (!mIsUserUnlocked) { - mUserUnlockedReceiver.register(mContext, ACTION_USER_UNLOCKED); - } - runOnDestroy(() -> mUserUnlockedReceiver.unregisterReceiverSafely(mContext)); - // Register for exclusion updates mExclusionListener = new SystemGestureExclusionListenerCompat(mDisplayId) { @Override @@ -309,25 +289,6 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener { return mDisplayId; } - /** - * Adds a callback for when a user is unlocked. If the user is already unlocked, this listener - * will be called back immediately. - */ - public void runOnUserUnlocked(Runnable action) { - if (mIsUserUnlocked) { - action.run(); - } else { - mUserUnlockedActions.add(action); - } - } - - /** - * @return whether the user is unlocked. - */ - public boolean isUserUnlocked() { - return mIsUserUnlocked; - } - /** * @return whether the user has completed setup wizard */ @@ -335,14 +296,6 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener { return mIsUserSetupComplete; } - private void notifyUserUnlocked() { - for (Runnable action : mUserUnlockedActions) { - action.run(); - } - mUserUnlockedActions.clear(); - mUserUnlockedReceiver.unregisterReceiverSafely(mContext); - } - /** * Sets the task id where gestures should be blocked */ @@ -585,7 +538,7 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener { pw.println(" assistantAvailable=" + mAssistantAvailable); pw.println(" assistantDisabled=" + QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)); - pw.println(" isUserUnlocked=" + mIsUserUnlocked); + pw.println(" isUserUnlocked=" + LockedUserState.get(mContext).isUserUnlocked()); pw.println(" isOneHandedModeEnabled=" + mIsOneHandedModeEnabled); pw.println(" isSwipeToNotificationEnabled=" + mIsSwipeToNotificationEnabled); pw.println(" deferredGestureRegion=" + mDeferredGestureRegion.getBounds()); diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 5d17cc77f4..65eac6d86e 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -87,6 +87,7 @@ import com.android.launcher3.tracing.LauncherTraceProto; import com.android.launcher3.tracing.TouchInteractionServiceProto; import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.util.DisplayController; +import com.android.launcher3.util.LockedUserState; import com.android.launcher3.util.OnboardingPrefs; import com.android.launcher3.util.TraceHelper; import com.android.quickstep.inputconsumers.AccessibilityInputConsumer; @@ -406,8 +407,8 @@ public class TouchInteractionService extends Service mRotationTouchHelper = mDeviceState.getRotationTouchHelper(); // Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized. - mDeviceState.runOnUserUnlocked(this::onUserUnlocked); - mDeviceState.runOnUserUnlocked(mTaskbarManager::onUserUnlocked); + LockedUserState.get(this).runOnUserUnlocked(this::onUserUnlocked); + LockedUserState.get(this).runOnUserUnlocked(mTaskbarManager::onUserUnlocked); mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged); ProtoTracer.INSTANCE.get(this).add(this); @@ -477,7 +478,7 @@ public class TouchInteractionService extends Service } private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() { - if (!mDeviceState.isUserUnlocked() || mDeviceState.isButtonNavMode()) { + if (!LockedUserState.get(this).isUserUnlocked() || mDeviceState.isButtonNavMode()) { // Skip if not yet unlocked (can't read user shared prefs) or if the current navigation // mode doesn't have gestures return; @@ -520,7 +521,7 @@ public class TouchInteractionService extends Service @UiThread private void onSystemUiFlagsChanged(int lastSysUIFlags) { - if (mDeviceState.isUserUnlocked()) { + if (LockedUserState.get(this).isUserUnlocked()) { int systemUiStateFlags = mDeviceState.getSystemUiStateFlags(); SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags); mOverviewComponentObserver.onSystemUiStateChanged(); @@ -565,7 +566,7 @@ public class TouchInteractionService extends Service @UiThread private void onAssistantVisibilityChanged() { - if (mDeviceState.isUserUnlocked()) { + if (LockedUserState.get(this).isUserUnlocked()) { mOverviewComponentObserver.getActivityInterface().onAssistantVisibilityChanged( mDeviceState.getAssistantVisibility()); } @@ -575,7 +576,7 @@ public class TouchInteractionService extends Service public void onDestroy() { Log.d(TAG, "Touch service destroyed: user=" + getUserId()); sIsInitialized = false; - if (mDeviceState.isUserUnlocked()) { + if (LockedUserState.get(this).isUserUnlocked()) { mInputConsumer.unregisterInputConsumer(); mOverviewComponentObserver.onDestroy(); } @@ -609,7 +610,7 @@ public class TouchInteractionService extends Service TestLogging.recordMotionEvent( TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event); - if (!mDeviceState.isUserUnlocked()) { + if (!LockedUserState.get(this).isUserUnlocked()) { return; } @@ -631,7 +632,8 @@ public class TouchInteractionService extends Service mGestureState = newGestureState; mConsumer = newConsumer(prevGestureState, mGestureState, event); mUncheckedConsumer = mConsumer; - } else if (mDeviceState.isUserUnlocked() && mDeviceState.isFullyGesturalNavMode() + } else if (LockedUserState.get(this).isUserUnlocked() + && mDeviceState.isFullyGesturalNavMode() && mDeviceState.canTriggerAssistantAction(event)) { mGestureState = createGestureState(mGestureState); // Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we @@ -751,7 +753,7 @@ public class TouchInteractionService extends Service boolean canStartSystemGesture = mDeviceState.canStartSystemGesture(); - if (!mDeviceState.isUserUnlocked()) { + if (!LockedUserState.get(this).isUserUnlocked()) { CompoundString reasonString = newCompoundString("device locked"); InputConsumer consumer; if (canStartSystemGesture) { @@ -1098,7 +1100,7 @@ public class TouchInteractionService extends Service } private void preloadOverview(boolean fromInit, boolean forSUWAllSet) { - if (!mDeviceState.isUserUnlocked()) { + if (!LockedUserState.get(this).isUserUnlocked()) { return; } @@ -1130,7 +1132,7 @@ public class TouchInteractionService extends Service @Override public void onConfigurationChanged(Configuration newConfig) { - if (!mDeviceState.isUserUnlocked()) { + if (!LockedUserState.get(this).isUserUnlocked()) { return; } final BaseActivityInterface activityInterface = @@ -1171,7 +1173,7 @@ public class TouchInteractionService extends Service } else { // Dump everything FeatureFlags.dump(pw); - if (mDeviceState.isUserUnlocked()) { + if (LockedUserState.get(this).isUserUnlocked()) { PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw); } mDeviceState.dump(pw); diff --git a/src/com/android/launcher3/util/LockedUserState.kt b/src/com/android/launcher3/util/LockedUserState.kt new file mode 100644 index 0000000000..7b49583b86 --- /dev/null +++ b/src/com/android/launcher3/util/LockedUserState.kt @@ -0,0 +1,57 @@ +package com.android.launcher3.util + +import android.content.Context +import android.content.Intent +import android.os.Process +import android.os.UserManager +import androidx.annotation.VisibleForTesting + +class LockedUserState(private val mContext: Context) : SafeCloseable { + var isUserUnlocked: Boolean + private set + private val mUserUnlockedActions: RunnableList = RunnableList() + + @VisibleForTesting + val mUserUnlockedReceiver = SimpleBroadcastReceiver { + if (Intent.ACTION_USER_UNLOCKED == it.action) { + isUserUnlocked = true + notifyUserUnlocked() + } + } + + init { + isUserUnlocked = + mContext + .getSystemService(UserManager::class.java)!! + .isUserUnlocked(Process.myUserHandle()) + if (isUserUnlocked) { + notifyUserUnlocked() + } else { + mUserUnlockedReceiver.register(mContext, Intent.ACTION_USER_UNLOCKED) + } + } + + private fun notifyUserUnlocked() { + mUserUnlockedActions.executeAllAndDestroy() + mUserUnlockedReceiver.unregisterReceiverSafely(mContext) + } + + /** Stops the receiver from listening for ACTION_USER_UNLOCK broadcasts. */ + override fun close() { + mUserUnlockedReceiver.unregisterReceiverSafely(mContext) + } + + /** + * Adds a `Runnable` to be executed when a user is unlocked. If the user is already unlocked, + * this runnable will run immediately because RunnableList will already have been destroyed. + */ + fun runOnUserUnlocked(action: Runnable) { + mUserUnlockedActions.add(action) + } + + companion object { + @VisibleForTesting val INSTANCE = MainThreadInitializedObject { LockedUserState(it) } + + @JvmStatic fun get(context: Context): LockedUserState = INSTANCE.get(context) + } +} diff --git a/tests/src/com/android/launcher3/util/LockedUserStateTest.kt b/tests/src/com/android/launcher3/util/LockedUserStateTest.kt new file mode 100644 index 0000000000..84156e7101 --- /dev/null +++ b/tests/src/com/android/launcher3/util/LockedUserStateTest.kt @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.util + +import android.content.Context +import android.content.Intent +import android.os.Process +import android.os.UserManager +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyZeroInteractions +import org.mockito.Mockito.`when` +import org.mockito.MockitoAnnotations + +/** Unit tests for {@link LockedUserUtil} */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class LockedUserStateTest { + + @Mock lateinit var userManager: UserManager + @Mock lateinit var context: Context + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + `when`(context.getSystemService(UserManager::class.java)).thenReturn(userManager) + } + + @Test + fun runOnUserUnlocked_runs_action_immediately_if_already_unlocked() { + `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true) + LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context)) + val action: Runnable = mock() + + LockedUserState.get(context).runOnUserUnlocked(action) + verify(action).run() + } + + @Test + fun runOnUserUnlocked_waits_to_run_action_until_user_is_unlocked() { + `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false) + LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context)) + val action: Runnable = mock() + + LockedUserState.get(context).runOnUserUnlocked(action) + verifyZeroInteractions(action) + + LockedUserState.get(context) + .mUserUnlockedReceiver + .onReceive(context, Intent(Intent.ACTION_USER_UNLOCKED)) + + verify(action).run() + } + + @Test + fun isUserUnlocked_returns_true_when_user_is_unlocked() { + `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true) + LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context)) + assertThat(LockedUserState.get(context).isUserUnlocked).isTrue() + } + + @Test + fun isUserUnlocked_returns_false_when_user_is_locked() { + `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false) + LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context)) + assertThat(LockedUserState.get(context).isUserUnlocked).isFalse() + } +} From e82476ad3b7995590a0509a5f7700405c05272fb Mon Sep 17 00:00:00 2001 From: Stefan Andonian Date: Tue, 10 Jan 2023 22:46:31 +0000 Subject: [PATCH 027/469] Rename LoaderResults to LauncherBinder. LoaderResults implies a data object, not binding code specific to Launcher UX containers. This CL merely renames this class and its usages. It also adds a comment that explains why the functionality is split into a base class and an implementing class. Bug: 251502424 Test: Compilation worked correctly. Change-Id: I01b5ac2f717e9b20612538d5c1e0ca947beb593f --- ...LoaderResults.java => LauncherBinder.java} | 6 +++--- src/com/android/launcher3/LauncherModel.java | 14 ++++++------- ...erResults.java => BaseLauncherBinder.java} | 21 ++++++++++++++----- .../android/launcher3/model/LoaderTask.java | 16 +++++++------- ...LoaderResults.java => LauncherBinder.java} | 6 +++--- 5 files changed, 37 insertions(+), 26 deletions(-) rename go/src/com/android/launcher3/model/{LoaderResults.java => LauncherBinder.java} (82%) rename src/com/android/launcher3/model/{BaseLoaderResults.java => BaseLauncherBinder.java} (95%) rename src_shortcuts_overrides/com/android/launcher3/model/{LoaderResults.java => LauncherBinder.java} (88%) diff --git a/go/src/com/android/launcher3/model/LoaderResults.java b/go/src/com/android/launcher3/model/LauncherBinder.java similarity index 82% rename from go/src/com/android/launcher3/model/LoaderResults.java rename to go/src/com/android/launcher3/model/LauncherBinder.java index 5f71061a6e..437d8caf94 100644 --- a/go/src/com/android/launcher3/model/LoaderResults.java +++ b/go/src/com/android/launcher3/model/LauncherBinder.java @@ -22,11 +22,11 @@ import com.android.launcher3.LauncherAppState; import com.android.launcher3.model.BgDataModel.Callbacks; /** - * Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}. + * Binds the results of {@link com.android.launcher3.model.LoaderTask} to the Callbacks objects. */ -public class LoaderResults extends BaseLoaderResults { +public class LauncherBinder extends BaseLauncherBinder { - public LoaderResults(LauncherAppState app, BgDataModel dataModel, + public LauncherBinder(LauncherAppState app, BgDataModel dataModel, AllAppsList allAppsList, Callbacks[] callbacks) { super(app, dataModel, allAppsList, callbacks, MAIN_EXECUTOR); } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 20df89763b..2c6458bc49 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -47,7 +47,7 @@ import com.android.launcher3.model.BgDataModel; import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.model.CacheDataUpdatedTask; import com.android.launcher3.model.ItemInstallQueue; -import com.android.launcher3.model.LoaderResults; +import com.android.launcher3.model.LauncherBinder; import com.android.launcher3.model.LoaderTask; import com.android.launcher3.model.ModelDelegate; import com.android.launcher3.model.ModelWriter; @@ -405,22 +405,22 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi MAIN_EXECUTOR.execute(cb::clearPendingBinds); } - LoaderResults loaderResults = new LoaderResults( + LauncherBinder launcherBinder = new LauncherBinder( mApp, mBgDataModel, mBgAllAppsList, callbacksList); if (bindDirectly) { // Divide the set of loaded items into those that we are binding synchronously, // and everything else that is to be bound normally (asynchronously). - loaderResults.bindWorkspace(bindAllCallbacks); + launcherBinder.bindWorkspace(bindAllCallbacks); // For now, continue posting the binding of AllApps as there are other // issues that arise from that. - loaderResults.bindAllApps(); - loaderResults.bindDeepShortcuts(); - loaderResults.bindWidgets(); + launcherBinder.bindAllApps(); + launcherBinder.bindDeepShortcuts(); + launcherBinder.bindWidgets(); return true; } else { stopLoader(); mLoaderTask = new LoaderTask( - mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, loaderResults); + mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, launcherBinder); // Always post the loader task, instead of running directly // (even on same thread) so that we exit any nested synchronized blocks diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLauncherBinder.java similarity index 95% rename from src/com/android/launcher3/model/BaseLoaderResults.java rename to src/com/android/launcher3/model/BaseLauncherBinder.java index 8c6428b028..9f8db51629 100644 --- a/src/com/android/launcher3/model/BaseLoaderResults.java +++ b/src/com/android/launcher3/model/BaseLauncherBinder.java @@ -47,12 +47,11 @@ import java.util.Objects; import java.util.concurrent.Executor; /** - * Base Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}. + * Binds the results of {@link com.android.launcher3.model.LoaderTask} to the Callbacks objects. */ -public abstract class BaseLoaderResults { +public abstract class BaseLauncherBinder { - protected static final String TAG = "LoaderResults"; - protected static final int INVALID_SCREEN_ID = -1; + protected static final String TAG = "LauncherBinder"; private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons protected final LooperExecutor mUiExecutor; @@ -65,7 +64,7 @@ public abstract class BaseLoaderResults { private int mMyBindingId; - public BaseLoaderResults(LauncherAppState app, BgDataModel dataModel, + public BaseLauncherBinder(LauncherAppState app, BgDataModel dataModel, AllAppsList allAppsList, Callbacks[] callbacksList, LooperExecutor uiExecutor) { mUiExecutor = uiExecutor; mApp = app; @@ -101,8 +100,14 @@ public abstract class BaseLoaderResults { } } + /** + * BindDeepShortcuts is abstract because it is a no-op for the go launcher. + */ public abstract void bindDeepShortcuts(); + /** + * Binds the all apps results from LoaderTask to the callbacks UX. + */ public void bindAllApps() { // shallow copy AppInfo[] apps = mBgAllAppsList.copyData(); @@ -110,6 +115,9 @@ public abstract class BaseLoaderResults { executeCallbacksTask(c -> c.bindAllApplications(apps, flags), mUiExecutor); } + /** + * bindWidgets is abstract because it is a no-op for the go launcher. + */ public abstract void bindWidgets(); /** @@ -160,6 +168,9 @@ public abstract class BaseLoaderResults { }); } + /** + * Only used in LoaderTask. + */ public LooperIdleLock newIdleLock(Object lock) { LooperIdleLock idleLock = new LooperIdleLock(lock, mUiExecutor.getLooper()); // If we are not binding or if the main looper is already idle, there is no reason to wait diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 1d6971e27d..46a6a66c76 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -125,7 +125,7 @@ public class LoaderTask implements Runnable { private FirstScreenBroadcast mFirstScreenBroadcast; - private final LoaderResults mResults; + private final LauncherBinder mLauncherBinder; private final LauncherApps mLauncherApps; private final UserManager mUserManager; @@ -145,12 +145,12 @@ public class LoaderTask implements Runnable { private String mDbName; public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel, - ModelDelegate modelDelegate, LoaderResults results) { + ModelDelegate modelDelegate, LauncherBinder launcherBinder) { mApp = app; mBgAllAppsList = bgAllAppsList; mBgDataModel = dataModel; mModelDelegate = modelDelegate; - mResults = results; + mLauncherBinder = launcherBinder; mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class); mUserManager = mApp.getContext().getSystemService(UserManager.class); @@ -163,7 +163,7 @@ public class LoaderTask implements Runnable { // Wait until the either we're stopped or the other threads are done. // This way we don't start loading all apps until the workspace has settled // down. - LooperIdleLock idleLock = mResults.newIdleLock(this); + LooperIdleLock idleLock = mLauncherBinder.newIdleLock(this); // Just in case mFlushingWorkerThread changes but we aren't woken up, // wait no longer than 1sec at a time while (!mStopped && idleLock.awaitLocked(1000)); @@ -221,7 +221,7 @@ public class LoaderTask implements Runnable { } verifyNotStopped(); - mResults.bindWorkspace(true /* incrementBindId */); + mLauncherBinder.bindWorkspace(true /* incrementBindId */); logASplit(logger, "bindWorkspace"); mModelDelegate.workspaceLoadComplete(); @@ -245,7 +245,7 @@ public class LoaderTask implements Runnable { logASplit(logger, "loadAllApps"); verifyNotStopped(); - mResults.bindAllApps(); + mLauncherBinder.bindAllApps(); logASplit(logger, "bindAllApps"); verifyNotStopped(); @@ -271,7 +271,7 @@ public class LoaderTask implements Runnable { logASplit(logger, "loadDeepShortcuts"); verifyNotStopped(); - mResults.bindDeepShortcuts(); + mLauncherBinder.bindDeepShortcuts(); logASplit(logger, "bindDeepShortcuts"); verifyNotStopped(); @@ -290,7 +290,7 @@ public class LoaderTask implements Runnable { logASplit(logger, "load widgets"); verifyNotStopped(); - mResults.bindWidgets(); + mLauncherBinder.bindWidgets(); logASplit(logger, "bindWidgets"); verifyNotStopped(); diff --git a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java b/src_shortcuts_overrides/com/android/launcher3/model/LauncherBinder.java similarity index 88% rename from src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java rename to src_shortcuts_overrides/com/android/launcher3/model/LauncherBinder.java index abce2a24f4..e1a5f24148 100644 --- a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java +++ b/src_shortcuts_overrides/com/android/launcher3/model/LauncherBinder.java @@ -27,11 +27,11 @@ import java.util.HashMap; import java.util.List; /** - * Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}. + * Binds the results of {@link com.android.launcher3.model.LoaderTask} to the Callbacks objects. */ -public class LoaderResults extends BaseLoaderResults { +public class LauncherBinder extends BaseLauncherBinder { - public LoaderResults(LauncherAppState app, BgDataModel dataModel, + public LauncherBinder(LauncherAppState app, BgDataModel dataModel, AllAppsList allAppsList, Callbacks[] callbacks) { super(app, dataModel, allAppsList, callbacks, MAIN_EXECUTOR); } From e884c2c79f74f22cda1293371e4a422c14a81ef0 Mon Sep 17 00:00:00 2001 From: Fengjiang Li Date: Mon, 19 Dec 2022 14:42:14 -0800 Subject: [PATCH 028/469] Support predictive back from all apps to home bug: b/238475505 Test: manual Change-Id: Ibf4f7f41a26b044a538e2cd566d2297ed88f1b99 --- .../uioverrides/QuickstepLauncher.java | 29 ++++++++++ src/com/android/launcher3/BaseActivity.java | 20 ++++--- src/com/android/launcher3/Launcher.java | 9 +++ src/com/android/launcher3/LauncherState.java | 23 ++++++++ .../allapps/AllAppsTransitionController.java | 56 +++++++++++++++++++ .../allapps/BaseAllAppsContainerView.java | 6 +- .../android/launcher3/anim/Interpolators.java | 2 + .../launcher3/statemanager/StateManager.java | 24 ++++++++ .../android/launcher3/views/ScrimView.java | 14 ++++- 9 files changed, 171 insertions(+), 12 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 28c8980668..8bd72d83be 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -69,9 +69,13 @@ import android.view.HapticFeedbackConstants; import android.view.RemoteAnimationTarget; import android.view.View; import android.view.WindowManagerGlobal; +import android.window.BackEvent; +import android.window.OnBackAnimationCallback; +import android.window.OnBackInvokedDispatcher; import android.window.SplashScreen; import androidx.annotation.BinderThread; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.app.viewcapture.ViewCapture; @@ -105,6 +109,8 @@ import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory; import com.android.launcher3.statemanager.StateManager.StateHandler; import com.android.launcher3.taskbar.LauncherTaskbarUIController; import com.android.launcher3.taskbar.TaskbarManager; +import com.android.launcher3.testing.TestLogging; +import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.uioverrides.QuickstepWidgetHolder.QuickstepHolderFactory; import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory; import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController; @@ -623,6 +629,29 @@ public class QuickstepLauncher extends Launcher { } } + @Override + protected void registerBackDispatcher() { + getOnBackInvokedDispatcher().registerOnBackInvokedCallback( + OnBackInvokedDispatcher.PRIORITY_DEFAULT, + new OnBackAnimationCallback() { + @Override + public void onBackInvoked() { + onBackPressed(); + TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked"); + } + + @Override + public void onBackProgressed(@NonNull BackEvent backEvent) { + QuickstepLauncher.this.onBackProgressed(backEvent.getProgress()); + } + + @Override + public void onBackCancelled() { + QuickstepLauncher.this.onBackCancelled(); + } + }); + } + private void onTaskbarInAppDisplayProgressUpdate(float progress, int flag) { if (mTaskbarManager == null || mTaskbarManager.getCurrentActivityContext() == null diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java index 61707dff49..e71391f907 100644 --- a/src/com/android/launcher3/BaseActivity.java +++ b/src/com/android/launcher3/BaseActivity.java @@ -176,14 +176,7 @@ public abstract class BaseActivity extends Activity implements ActivityContext { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (Utilities.ATLEAST_T) { - getOnBackInvokedDispatcher().registerOnBackInvokedCallback( - OnBackInvokedDispatcher.PRIORITY_DEFAULT, - () -> { - onBackPressed(); - TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked"); - }); - } + registerBackDispatcher(); } @Override @@ -246,6 +239,17 @@ public abstract class BaseActivity extends Activity implements ActivityContext { } + protected void registerBackDispatcher() { + if (Utilities.ATLEAST_T) { + getOnBackInvokedDispatcher().registerOnBackInvokedCallback( + OnBackInvokedDispatcher.PRIORITY_DEFAULT, + () -> { + onBackPressed(); + TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked"); + }); + } + } + public boolean isStarted() { return (mActivityFlags & ACTIVITY_STATE_STARTED) != 0; } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 43772e4279..1563130493 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -120,6 +120,7 @@ import android.widget.ImageView; import android.widget.Toast; import androidx.annotation.CallSuper; +import androidx.annotation.FloatRange; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; @@ -2065,6 +2066,14 @@ public class Launcher extends StatefulActivity mStateManager.getState().onBackPressed(this); } + protected void onBackProgressed(@FloatRange(from = 0.0, to = 1.0) float backProgress) { + mStateManager.getState().onBackProgressed(this, backProgress); + } + + protected void onBackCancelled() { + mStateManager.getState().onBackCancelled(this); + } + protected void onScreenOff() { // Reset AllApps to its initial state only if we are not in the middle of // processing a multi-step drop diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index 5dddc6f458..b9e4c175e2 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -34,6 +34,8 @@ import android.content.Context; import android.graphics.Color; import android.view.animation.Interpolator; +import androidx.annotation.FloatRange; + import com.android.launcher3.statemanager.BaseState; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.states.HintState; @@ -342,6 +344,27 @@ public abstract class LauncherState implements BaseState { } } + /** + * Find {@link StateManager} and target {@link LauncherState} to handle back progress in + * predictive back gesture. + */ + public void onBackProgressed( + Launcher launcher, @FloatRange(from = 0.0, to = 1.0) float backProgress) { + StateManager lsm = launcher.getStateManager(); + LauncherState toState = lsm.getLastState(); + lsm.onBackProgressed(toState, backProgress); + } + + /** + * Find {@link StateManager} and target {@link LauncherState} to handle backProgress in + * predictive back gesture. + */ + public void onBackCancelled(Launcher launcher) { + StateManager lsm = launcher.getStateManager(); + LauncherState toState = lsm.getLastState(); + lsm.onBackCancelled(toState); + } + public static abstract class PageAlphaProvider { public final Interpolator interpolator; diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 9930abeb12..24bfedb037 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.allapps; +import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT; @@ -33,11 +34,15 @@ import android.view.HapticFeedbackConstants; import android.view.View; import android.view.animation.Interpolator; +import androidx.annotation.FloatRange; + import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; +import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.anim.AnimatorListeners; +import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.statemanager.StateManager.StateHandler; @@ -61,6 +66,8 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener { // This constant should match the second derivative of the animator interpolator. public static final float INTERP_COEFF = 1.7f; + private static final float SWIPE_ALL_APPS_TO_HOME_MIN_SCALE = 0.9f; + private static final int REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS = 200; public static final FloatProperty ALL_APPS_PROGRESS = new FloatProperty("allAppsProgress") { @@ -139,6 +146,7 @@ public class AllAppsTransitionController private ActivityAllAppsContainerView mAppsView; private final Launcher mLauncher; + private final AnimatedFloat mAllAppScale = new AnimatedFloat(this::onScaleProgressChanged); private boolean mIsVerticalLayout; // Whether this class should take care of closing the keyboard. @@ -232,6 +240,52 @@ public class AllAppsTransitionController onProgressAnimationEnd(); } + @Override + public void onBackProgressed( + LauncherState toState, @FloatRange(from = 0.0, to = 1.0) float backProgress) { + if (!mLauncher.isInState(ALL_APPS) || !NORMAL.equals(toState)) { + return; + } + + float deceleratedProgress = + Interpolators.PREDICTIVE_BACK_DECELERATED_EASE.getInterpolation(backProgress); + float scaleProgress = SWIPE_ALL_APPS_TO_HOME_MIN_SCALE + + (1 - SWIPE_ALL_APPS_TO_HOME_MIN_SCALE) * (1 - deceleratedProgress); + + mAllAppScale.updateValue(scaleProgress); + } + + @Override + public void onBackCancelled(LauncherState toState) { + if (!mLauncher.isInState(ALL_APPS) || !NORMAL.equals(toState)) { + return; + } + + // TODO: once ag/20649618 is picked into tm-qpr, we don't need to animate back on cancel + // swipe because framework will do that for us in {@link #onBackProgressed}. + animateAllAppsToNoScale(); + } + + private void onScaleProgressChanged() { + final float scaleProgress = mAllAppScale.value; + SCALE_PROPERTY.set(mLauncher.getAppsView(), scaleProgress); + mLauncher.getScrimView().setScrimHeaderScale(scaleProgress); + + AllAppsRecyclerView rv = mLauncher.getAppsView().getActiveRecyclerView(); + if (rv != null && rv.getScrollbar() != null) { + rv.getScrollbar().setVisibility(scaleProgress < 1f ? View.INVISIBLE : View.VISIBLE); + } + + // TODO(b/264906511): We need to disable view clipping on all apps' parent views so + // that the extra roll of app icons are displayed. + } + + private void animateAllAppsToNoScale() { + mAllAppScale.animateToValue(1f) + .setDuration(REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS) + .start(); + } + /** * Creates an animation which updates the vertical transition progress and updates all the * dependent UI using various animation events @@ -258,6 +312,8 @@ public class AllAppsTransitionController if (config.userControlled && success && mShouldControlKeyboard) { mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard(); } + + mAllAppScale.updateValue(1f); }); } diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java index 00e89bacc5..487807751d 100644 --- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java @@ -808,7 +808,7 @@ public abstract class BaseAllAppsContainerView> { } } + /** Handles backProgress in predictive back gesture by passing it to state handlers. */ + public void onBackProgressed( + STATE_TYPE toState, @FloatRange(from = 0.0, to = 1.0) float backProgress) { + for (StateHandler handler : getStateHandlers()) { + handler.onBackProgressed(toState, backProgress); + } + } + + /** Handles back cancelled event in predictive back gesture by passing it to state handlers. */ + public void onBackCancelled(STATE_TYPE toState) { + for (StateHandler handler : getStateHandlers()) { + handler.onBackCancelled(toState); + } + } + private void goToState( STATE_TYPE state, boolean animated, long delay, AnimatorListener listener) { animated &= areAnimatorsEnabled(); @@ -586,6 +603,13 @@ public class StateManager> { */ void setStateWithAnimation( STATE_TYPE toState, StateAnimationConfig config, PendingAnimation animation); + + /** Handles backProgress in predictive back gesture for target state. */ + default void onBackProgressed( + STATE_TYPE toState, @FloatRange(from = 0.0, to = 1.0) float backProgress) {}; + + /** Handles back cancelled event in predictive back gesture for target state. */ + default void onBackCancelled(STATE_TYPE toState) {}; } public interface StateListener { diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java index 4c0bfde682..870ff122e1 100644 --- a/src/com/android/launcher3/views/ScrimView.java +++ b/src/com/android/launcher3/views/ScrimView.java @@ -46,6 +46,7 @@ public class ScrimView extends View implements Insettable { private int mBackgroundColor; private boolean mIsVisible = true; private boolean mLastDispatchedOpaqueness; + private float mHeaderScale = 1f; public ScrimView(Context context, AttributeSet attrs) { super(context, attrs); @@ -91,7 +92,16 @@ public class ScrimView extends View implements Insettable { protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mDrawingController != null) { - mDrawingController.drawOnScrim(canvas); + mDrawingController.drawOnScrimWithScale(canvas, mHeaderScale); + } + } + + /** Set scrim header's scale and bottom offset. */ + public void setScrimHeaderScale(float scale) { + boolean hasChanged = mHeaderScale != scale; + mHeaderScale = scale; + if (hasChanged) { + invalidate(); } } @@ -176,6 +186,6 @@ public class ScrimView extends View implements Insettable { /** * Called inside ScrimView#OnDraw */ - void drawOnScrim(Canvas canvas); + void drawOnScrimWithScale(Canvas canvas, float scale); } } From 9d46bca1b92143259413c0262bb87bc9dbad45b8 Mon Sep 17 00:00:00 2001 From: Christophe Pinelli Date: Wed, 11 Jan 2023 19:43:41 +0000 Subject: [PATCH 029/469] Make mutable PendingIntent explicit Starting from target SDK U, we will block creation of mutable PendingIntents with implicit Intents because attackers can mutate the Intent object within and launch altered behavior on behalf of victim apps. For more details on the vulnerability, see go/pendingintent-rca. From a quick analysis, we concluded that the PendingIntent here was only destined to the test app/to the app, so it was made explicit. Reviewers, please call out if this is not the case. Bug: 236704164 Test: atest NexusLauncherTests:com.android.quickstep.DigitalWellBeingToastTest Test: atest RequestPinItemTest Change-Id: I81311b0588b7b712c97de6cb016a62bb91c7a77c --- .../src/com/android/quickstep/DigitalWellBeingToastTest.java | 3 ++- .../com/android/launcher3/ui/widget/RequestPinItemTest.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java index 5c2e14f119..1129a337e3 100644 --- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java +++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java @@ -46,7 +46,8 @@ public class DigitalWellBeingToastTest extends AbstractQuickStepTest { runWithShellPermission(() -> usageStatsManager.registerAppUsageLimitObserver(observerId, packages, Duration.ofSeconds(600), Duration.ofSeconds(300), - PendingIntent.getActivity(mTargetContext, -1, new Intent(), + PendingIntent.getActivity(mTargetContext, -1, new Intent() + .setPackage(mTargetContext.getPackageName()), PendingIntent.FLAG_MUTABLE))); mLauncher.goHome(); diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java index 0db719e349..96690102d4 100644 --- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java +++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java @@ -147,7 +147,8 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { // Set callback PendingIntent callback = PendingIntent.getBroadcast(mTargetContext, 0, - new Intent(mCallbackAction), FLAG_ONE_SHOT | FLAG_MUTABLE); + new Intent(mCallbackAction).setPackage(mTargetContext.getPackageName()), + FLAG_ONE_SHOT | FLAG_MUTABLE); mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent( RequestPinItemActivity.class, "setCallback").putExtra( RequestPinItemActivity.EXTRA_PARAM + "0", callback)); From 1d7e050bc6492c10b5ff3e02d7621cd8902aa096 Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Wed, 11 Jan 2023 21:29:37 -0800 Subject: [PATCH 030/469] Respect nav button tinting in SUW * Check was originally added with rationale in b/204384193#comment26, however presently we do provide insets to SUW as of b/253538830 so looks like now we are getting the correct info from SysUI * There is a possibly related issue where buttons are not tinted correctly on the final page of SUW (b/265238238) Test: Went through setup manually via adb and observed that on light theme with dark dialog scrim the button changes color adb shell am start -a android.intent.action.MAIN -n com.google.android.setupwizard/.SetupWizardTestActivity Bug: 256521774 Change-Id: I080033e21d22fbc366b7248b2d647fec610a5fb9 --- .../com/android/launcher3/taskbar/TaskbarActivityContext.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 731eea78e9..4e795d9090 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -598,9 +598,6 @@ public class TaskbarActivityContext extends BaseTaskbarContext { } public void onNavButtonsDarkIntensityChanged(float darkIntensity) { - if (!isUserSetupComplete()) { - return; - } mControllers.navbarButtonsViewController.getTaskbarNavButtonDarkIntensity() .updateValue(darkIntensity); } From 031fbcc5640f64143e798ee361e18bd3ff2fe371 Mon Sep 17 00:00:00 2001 From: Johannes Gallmann Date: Wed, 4 Jan 2023 14:16:47 +0100 Subject: [PATCH 031/469] Prevent workspace enter animation for predictive back Bug: 263913711 Test: Manual, i.e. testing predictive back for four scenarios: Back to all apps, Back to home. Both with the closed app icon visible or not. Change-Id: Id87686098e6ad413819dc75545071da00291db65 --- .../launcher3/QuickstepTransitionManager.java | 16 +++++++++------- .../LauncherBackAnimationController.java | 3 ++- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 2aa0af4113..71d1296aa8 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -1560,7 +1560,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener RemoteAnimationTarget[] wallpaperTargets, boolean fromUnlock, RectF startRect, - float startWindowCornerRadius) { + float startWindowCornerRadius, + boolean fromPredictiveBack) { AnimatorSet anim = null; RectFSpringAnim rectFSpringAnim = null; @@ -1594,7 +1595,11 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener rectFSpringAnim = getClosingWindowAnimators( anim, appTargets, launcherView, velocity, startRect, startWindowCornerRadius); - if (!mLauncher.isInState(LauncherState.ALL_APPS)) { + if (mLauncher.isInState(LauncherState.ALL_APPS)) { + // Skip scaling all apps, otherwise FloatingIconView will get wrong + // layout bounds. + skipAllAppsScale = true; + } else if (!fromPredictiveBack) { anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y, true /* animateOverviewScrim */, launcherView).getAnimators()); @@ -1606,10 +1611,6 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener // We play StaggeredWorkspaceAnim as a part of the closing window animation. playWorkspaceReveal = false; - } else { - // Skip scaling all apps, otherwise FloatingIconView will get wrong - // layout bounds. - skipAllAppsScale = true; } } else { anim.play(getFallbackClosingWindowAnimators(appTargets)); @@ -1686,7 +1687,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener new RectF(getWindowTargetBounds(appTargets, getRotationChange(appTargets))); Pair pair = createWallpaperOpenAnimations( appTargets, wallpaperTargets, mFromUnlock, windowTargetBounds, - QuickStepContract.getWindowCornerRadius(mLauncher)); + QuickStepContract.getWindowCornerRadius(mLauncher), + false /* fromPredictiveBack */); mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL); result.setAnimation(pair.second, mLauncher); diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java index 27417516a4..b3e1878ca4 100644 --- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java +++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java @@ -289,7 +289,8 @@ public class LauncherBackAnimationController { new RemoteAnimationTarget[0], false /* fromUnlock */, mCurrentRect, - cornerRadius); + cornerRadius, + mBackInProgress /* fromPredictiveBack */); startTransitionAnimations(pair.first, pair.second); mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL); } From 696238e08639ec986234632ac330d83b765bc271 Mon Sep 17 00:00:00 2001 From: Nicolo' Mazzucato Date: Tue, 10 Jan 2023 17:15:56 +0000 Subject: [PATCH 032/469] Provide single threaded executor to UnfoldUnfoldTransitionFactory This fixes a concurrency issue where HingeSensorAngleProvider was being stopped and started at the same time in a thread-pool after a fast fold/unfold, despite not providing concurrency guarantees. In sysui, the background executor provided was already single threaded, so no issue arisen. From Launcher, THREAD_POOL_EXECUTOR was provided. In a follow up cl, I'll add a @SingleThreadBackground annotation to the executor used in the unfold lib. Bug: 261320823 Test: manually stress tested fold/unfold. Change-Id: Iccf1f1f7246d8592d4d80a032479aa75f0050655 --- .../src/com/android/launcher3/taskbar/TaskbarManager.java | 5 ----- .../com/android/launcher3/uioverrides/QuickstepLauncher.java | 5 +---- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java index 86e191151a..98c45d5f43 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java @@ -32,7 +32,6 @@ import android.net.Uri; import android.os.Handler; import android.os.SystemProperties; import android.provider.Settings; -import android.util.Log; import android.view.Display; import androidx.annotation.NonNull; @@ -227,10 +226,6 @@ public class TaskbarManager { mActivity = activity; UnfoldTransitionProgressProvider unfoldTransitionProgressProvider = getUnfoldTransitionProgressProviderForActivity(activity); - if (unfoldTransitionProgressProvider == null) { - Log.e("b/261320823", "UnfoldTransitionProgressProvider null in setActivity. " - + "Unfold animation for launcher will not work."); - } mUnfoldProgressProvider.setSourceProvider(unfoldTransitionProgressProvider); if (mTaskbarActivityContext != null) { diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 28c8980668..0bc3c115e7 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -43,7 +43,6 @@ import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_O import static com.android.launcher3.testing.shared.TestProtocol.QUICK_SWITCH_STATE_ORDINAL; import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN; import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE; -import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY; @@ -63,7 +62,6 @@ import android.os.Bundle; import android.os.CancellationSignal; import android.os.IBinder; import android.os.SystemProperties; -import android.util.Log; import android.view.Display; import android.view.HapticFeedbackConstants; import android.view.RemoteAnimationTarget; @@ -722,7 +720,7 @@ public class QuickstepLauncher extends Launcher { getSystemService(SensorManager.class), getMainThreadHandler(), getMainExecutor(), - /* backgroundExecutor= */ THREAD_POOL_EXECUTOR, + /* backgroundExecutor= */ UI_HELPER_EXECUTOR, /* tracingTagPrefix= */ "launcher", WindowManagerGlobal.getWindowManagerService() ); @@ -739,7 +737,6 @@ public class QuickstepLauncher extends Launcher { mUnfoldTransitionProgressProvider, mRotationChangeProvider ); - Log.d("b/261320823", "initUnfoldTransitionProgressProvider completed"); } } From 9db35f7a371634a17bde32699186f44b1564b9a2 Mon Sep 17 00:00:00 2001 From: Sebastian Franco Date: Tue, 27 Dec 2022 18:39:10 +0000 Subject: [PATCH 033/469] Remove unused Throw Throwable from clearHomescreen method Test: presubmit Bug: 263802298 Change-Id: I4b81e044eb4f6a84730fb4d61403cbfcadefd282 --- .../com/android/launcher3/celllayout/TestWorkspaceBuilder.java | 2 +- tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java b/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java index 16448afda0..7e3588b944 100644 --- a/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java +++ b/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java @@ -120,7 +120,7 @@ public class TestWorkspaceBuilder { * be clean otherwise this doesn't overrides the existing icons. */ public FavoriteItemsTransaction fillHotseatIcons(FavoriteItemsTransaction transaction) { - int hotseatCount = InvariantDeviceProfile.INSTANCE.get(mContext).numShownHotseatIcons; + int hotseatCount = InvariantDeviceProfile.INSTANCE.get(mContext).numDatabaseHotseatIcons; for (int i = 0; i < hotseatCount; i++) { transaction.addItem(getHotseatValues(i)); } diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 70d122bc90..cf21dd723f 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -299,7 +299,7 @@ public abstract class AbstractLauncherUiTest { /** * Removes all icons from homescreen and hotseat. */ - public void clearHomescreen() throws Throwable { + public void clearHomescreen() { LauncherSettings.Settings.call(mTargetContext.getContentResolver(), LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); LauncherSettings.Settings.call(mTargetContext.getContentResolver(), From 7d192c0e71212cf0e1bbc35f0c21c6754eb6c297 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Thu, 5 Jan 2023 19:51:16 +0000 Subject: [PATCH 034/469] Remove extra overview threshold if the transient taskbar is already open Test: Swipe to overview when taskbar is already opened vs not Fixes: 264555943 Change-Id: Ib4c31b085ca51e50fd7442ff9a27a5f47822c242 --- .../src/com/android/quickstep/AbsSwipeUpHandler.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 857ace25b1..36059437db 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -365,10 +365,13 @@ public abstract class AbsSwipeUpHandler, .getDimensionPixelSize(ENABLE_TASKBAR_REVISED_THRESHOLDS.get() ? R.dimen.taskbar_app_window_threshold_v2 : R.dimen.taskbar_app_window_threshold); - mTaskbarHomeOverviewThreshold = res.getDimensionPixelSize( - ENABLE_TASKBAR_REVISED_THRESHOLDS.get() - ? R.dimen.taskbar_home_overview_threshold_v2 - : R.dimen.taskbar_home_overview_threshold); + boolean swipeWillNotShowTaskbar = mTaskbarAlreadyOpen; + mTaskbarHomeOverviewThreshold = swipeWillNotShowTaskbar + ? 0 + : res.getDimensionPixelSize( + ENABLE_TASKBAR_REVISED_THRESHOLDS.get() + ? R.dimen.taskbar_home_overview_threshold_v2 + : R.dimen.taskbar_home_overview_threshold); mTaskbarCatchUpThreshold = res.getDimensionPixelSize(R.dimen.taskbar_catch_up_threshold); } From ca009e4e93e6dae7727fdef2d15c87e5a717e777 Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Fri, 6 Jan 2023 09:52:40 -0800 Subject: [PATCH 035/469] Skip depth and scrim if freeform tasks are visible If freeform tasks are shown, skip applying depth effect and scrim changes. These cause the background to flicker while showing freeform tasks and for example showing the transient taskbar. Bug: 263264985 Test: swipe up to show transient taskbar, observe launcher background does not blur or flicker to black Change-Id: I5b10d0f0c7065e903cb761488367c02d7e31d8b2 --- .../android/launcher3/uioverrides/QuickstepLauncher.java | 8 ++++++++ .../launcher3/uioverrides/states/BackgroundAppState.java | 7 +++++++ .../launcher3/uioverrides/states/QuickSwitchState.java | 9 +++++++++ src/com/android/launcher3/Launcher.java | 8 ++++++++ 4 files changed, 32 insertions(+) diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 28c8980668..72f321b5a8 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -1002,6 +1002,14 @@ public class QuickstepLauncher extends Launcher { mPendingSplitSelectInfo = null; } + @Override + public boolean areFreeformTasksVisible() { + if (mDesktopVisibilityController != null) { + return mDesktopVisibilityController.areFreeformTasksVisible(); + } + return false; + } + private static final class LauncherTaskViewController extends TaskViewTouchController { diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java index 733c6a8b48..95eb128e00 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java @@ -28,6 +28,7 @@ import com.android.launcher3.R; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.config.FeatureFlags; import com.android.quickstep.util.LayoutUtils; +import com.android.quickstep.views.DesktopTaskView; import com.android.quickstep.views.RecentsView; /** @@ -91,6 +92,12 @@ public class BackgroundAppState extends OverviewState { @Override protected float getDepthUnchecked(Context context) { + if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) { + if (Launcher.getLauncher(context).areFreeformTasksVisible()) { + // Don't blur the background while freeform tasks are visible + return 0; + } + } return 1; } diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java index 969abc2804..739246992c 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java @@ -17,10 +17,13 @@ package com.android.launcher3.uioverrides.states; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; +import android.graphics.Color; + import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.util.Themes; +import com.android.quickstep.views.DesktopTaskView; /** * State to indicate we are about to launch a recent task. Note that this state is only used when @@ -43,6 +46,12 @@ public class QuickSwitchState extends BackgroundAppState { @Override public int getWorkspaceScrimColor(Launcher launcher) { + if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) { + if (launcher.areFreeformTasksVisible()) { + // No scrim while freeform tasks are visible + return Color.TRANSPARENT; + } + } DeviceProfile dp = launcher.getDeviceProfile(); if (dp.isTaskbarPresentInApps) { return launcher.getColor(R.color.taskbar_background); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 43772e4279..03ddd6aa33 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -3317,4 +3317,12 @@ public class Launcher extends StatefulActivity return false; // Return false to continue iterating through all the items. }); } + + /** + * Returns {@code true} if there are visible tasks with windowing mode set to + * {@link android.app.WindowConfiguration#WINDOWING_MODE_FREEFORM} + */ + public boolean areFreeformTasksVisible() { + return false; // Base launcher does not track freeform tasks + } } From 9c5fa66d2f8b1fd2bcaf6b48fba7b8410f17e612 Mon Sep 17 00:00:00 2001 From: randypfohl Date: Fri, 6 Jan 2023 10:23:53 -0800 Subject: [PATCH 036/469] Cleaning up UI jank while quick switching Moving a few calls into the UI helper executor thread, as per the associated bug. duplicated the lines so the synchronous nature of clearing previous state wouldn't affect potential timings. Test: local testing, rotating screen. quick switching apps. Bug: 257470365 Change-Id: I59abad9ba51c0b19d467646752aa34ae7ef3ec1d --- quickstep/src/com/android/quickstep/RotationTouchHelper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java index f8b69666fe..4c66dbb66d 100644 --- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java +++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java @@ -321,9 +321,9 @@ public class RotationTouchHelper implements DisplayInfoChangeListener { if (enable && !mInOverview && !TestProtocol.sDisableSensorRotation) { // Clear any previous state from sensor manager mSensorRotation = mCurrentAppRotation; - mOrientationListener.enable(); + UI_HELPER_EXECUTOR.execute(mOrientationListener::enable); } else { - mOrientationListener.disable(); + UI_HELPER_EXECUTOR.execute(mOrientationListener::disable); } } From f4269f01d75602ce7debcf81d79fd6b7c1c02485 Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Thu, 12 Jan 2023 22:21:07 +0000 Subject: [PATCH 037/469] Revert "Add debug logs for hotseat disapperaed bug" This reverts commit 04658bcd42913aefacf8fa81d4bdf05db89cca15. Reason for revert: root cause found Bug: 260135164 Change-Id: I809662780d042923de015ff4cb810b3da5169f83 --- .../launcher3/taskbar/TaskbarLauncherStateController.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index 7a75661329..4ad3858de1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -160,7 +160,6 @@ import java.util.StringJoiner; mIconAlignment.finishAnimation(); - Log.d("b/260135164", "onDestroy - updateIconAlphaForHome(1)"); mLauncher.getHotseat().setIconsAlpha(1f); mLauncher.getStateManager().removeStateListener(mStateListener); @@ -405,8 +404,6 @@ import java.util.StringJoiner; public void onAnimationEnd(Animator animation) { if (isInStashedState && committed) { // Reset hotseat alpha to default - Log.d("b/260135164", - "playStateTransitionAnim#onAnimationEnd - setIconsAlpha(1)"); mLauncher.getHotseat().setIconsAlpha(1); } } @@ -455,9 +452,6 @@ import java.util.StringJoiner; * Hide Launcher Hotseat icons when Taskbar icons have opacity. Both icon sets * should not be visible at the same time. */ - Log.d("b/260135164", - "updateIconAlphaForHome - setIconsAlpha(" + (hotseatVisible ? 1 : 0) - + "), isTaskbarPresent: " + mLauncher.getDeviceProfile().isTaskbarPresent); mLauncher.getHotseat().setIconsAlpha(hotseatVisible ? 1 : 0); mLauncher.getHotseat().setQsbAlpha( mLauncher.getDeviceProfile().isQsbInline && !hotseatVisible ? 0 : 1); From 00e3c1ace403a83fb445312f066fa9b894ef1167 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 12 Jan 2023 16:02:12 -0800 Subject: [PATCH 038/469] Using AnimatedFloat for maintaing TaskbarInAppDisplayProgress instead of a float array Bug: 265352919 Test: Verified on device Change-Id: I603a817c18720d5f18fe0c2be7bdfd8cf2d6defc --- .../taskbar/LauncherTaskbarUIController.java | 65 ++++++------------- 1 file changed, 20 insertions(+), 45 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java index a18aabebab..237661e568 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -26,7 +26,6 @@ import android.animation.AnimatorSet; import android.annotation.ColorInt; import android.os.RemoteException; import android.util.Log; -import android.util.SparseArray; import android.view.TaskTransitionSpec; import android.view.WindowManagerGlobal; @@ -45,13 +44,13 @@ import com.android.launcher3.logging.InstanceIdSequence; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.util.DisplayController; +import com.android.launcher3.util.MultiPropertyFactory; import com.android.launcher3.util.OnboardingPrefs; import com.android.quickstep.RecentsAnimationCallbacks; import com.android.quickstep.views.RecentsView; import java.io.PrintWriter; import java.util.Set; -import java.util.stream.Stream; /** * A data source which integrates with a Launcher instance @@ -65,7 +64,12 @@ public class LauncherTaskbarUIController extends TaskbarUIController { public static final int WIDGETS_PAGE_PROGRESS_INDEX = 2; public static final int SYSUI_SURFACE_PROGRESS_INDEX = 3; - private final SparseArray mTaskbarInAppDisplayProgress = new SparseArray<>(4); + private static final int DISPLAY_PROGRESS_COUNT = 4; + + private final AnimatedFloat mTaskbarInAppDisplayProgress = new AnimatedFloat(); + private final MultiPropertyFactory mTaskbarInAppDisplayProgressMultiProp = + new MultiPropertyFactory<>(mTaskbarInAppDisplayProgress, + AnimatedFloat.VALUE, DISPLAY_PROGRESS_COUNT, Float::max); private final QuickstepLauncher mLauncher; @@ -305,11 +309,11 @@ public class LauncherTaskbarUIController extends TaskbarUIController { * 1 => use in-app layout */ public void onTaskbarInAppDisplayProgressUpdate(float progress, int progressIndex) { + mTaskbarInAppDisplayProgressMultiProp.get(progressIndex).setValue(progress); if (mControllers == null) { // This method can be called before init() is called. return; } - mTaskbarInAppDisplayProgress.put(progressIndex, progress); if (mControllers.uiController.isIconAlignedWithHotseat() && !mTaskbarLauncherStateController.isAnimatingToLauncher()) { // Only animate the nav buttons while home and not animating home, otherwise let @@ -317,30 +321,13 @@ public class LauncherTaskbarUIController extends TaskbarUIController { mControllers.navbarButtonsViewController .getTaskbarNavButtonTranslationYForInAppDisplay() .updateValue(mLauncher.getDeviceProfile().getTaskbarOffsetY() - * getInAppDisplayProgress()); + * mTaskbarInAppDisplayProgress.value); } } /** Returns true iff any in-app display progress > 0. */ public boolean shouldUseInAppLayout() { - return getInAppDisplayProgress() > 0; - } - - private float getInAppDisplayProgress(int index) { - if (!mTaskbarInAppDisplayProgress.contains(index)) { - mTaskbarInAppDisplayProgress.put(index, 0f); - } - return mTaskbarInAppDisplayProgress.get(index); - } - - private float getInAppDisplayProgress() { - return Stream.of( - getInAppDisplayProgress(MINUS_ONE_PAGE_PROGRESS_INDEX), - getInAppDisplayProgress(ALL_APPS_PAGE_PROGRESS_INDEX), - getInAppDisplayProgress(WIDGETS_PAGE_PROGRESS_INDEX), - getInAppDisplayProgress(SYSUI_SURFACE_PROGRESS_INDEX)) - .max(Float::compareTo) - .get(); + return mTaskbarInAppDisplayProgress.value > 0; } @Override @@ -358,7 +345,8 @@ public class LauncherTaskbarUIController extends TaskbarUIController { @Override public boolean isHotseatIconOnTopWhenAligned() { return mTaskbarLauncherStateController.isInHotseatOnTopStates() - && getInAppDisplayProgress(MINUS_ONE_PAGE_PROGRESS_INDEX) == 0; + && mTaskbarInAppDisplayProgressMultiProp.get(MINUS_ONE_PAGE_PROGRESS_INDEX) + .getValue() == 0; } @Override @@ -369,28 +357,15 @@ public class LauncherTaskbarUIController extends TaskbarUIController { "%s\tmTaskbarOverrideBackgroundAlpha=%.2f", prefix, mTaskbarOverrideBackgroundAlpha.value)); - pw.println(String.format("%s\tTaskbar in-app display progress:", prefix)); - if (mControllers == null) { - pw.println(String.format("%s\t\tMissing mControllers", prefix)); - } else { - pw.println(String.format( - "%s\t\tprogress at MINUS_ONE_PAGE_PROGRESS_INDEX=%.2f", - prefix, - getInAppDisplayProgress(MINUS_ONE_PAGE_PROGRESS_INDEX))); - pw.println(String.format( - "%s\t\tprogress at ALL_APPS_PAGE_PROGRESS_INDEX=%.2f", - prefix, - getInAppDisplayProgress(ALL_APPS_PAGE_PROGRESS_INDEX))); - pw.println(String.format( - "%s\t\tprogress at WIDGETS_PAGE_PROGRESS_INDEX=%.2f", - prefix, - getInAppDisplayProgress(WIDGETS_PAGE_PROGRESS_INDEX))); - pw.println(String.format( - "%s\t\tprogress at SYSUI_SURFACE_PROGRESS_INDEX=%.2f", - prefix, - getInAppDisplayProgress(SYSUI_SURFACE_PROGRESS_INDEX))); - } + mTaskbarInAppDisplayProgressMultiProp.dump( + prefix + "\t", + pw, + "mTaskbarInAppDisplayProgressMultiProp", + "MINUS_ONE_PAGE_PROGRESS_INDEX", + "ALL_APPS_PAGE_PROGRESS_INDEX", + "WIDGETS_PAGE_PROGRESS_INDEX", + "SYSUI_SURFACE_PROGRESS_INDEX"); mTaskbarLauncherStateController.dumpLogs(prefix + "\t", pw); } From 3f255eb591a78f137a37491ab3b2a4c84951bc44 Mon Sep 17 00:00:00 2001 From: Federico Baron Date: Thu, 22 Dec 2022 14:40:58 -0800 Subject: [PATCH 039/469] Workspace pagination UI update Here we improve the pagination visuals to follow the new design specs. We also polish and fix some issues with dot pagination in workspace. Bug: 262741688 Test: Turn on flag SHOW_DOT_PAGINATION, and test pagination on workspace. Verify that it follows the specs linked in the bug Change-Id: I626cdcf1f7392bcc88e9b4d6fd1df65246861445 --- res/values/dimens.xml | 1 + src/com/android/launcher3/Launcher.java | 11 +- .../launcher3/folder/FolderPagedView.java | 1 - .../pageindicators/PageIndicator.java | 7 ++ .../pageindicators/PageIndicatorDots.java | 103 ++++++++++++------ 5 files changed, 86 insertions(+), 37 deletions(-) diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 4d2e1b7c6d..d041dfe52b 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -252,6 +252,7 @@ 8dp + 6dp 10dp diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 43772e4279..517a2d351d 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -87,6 +87,7 @@ import android.content.pm.PackageManager; import android.content.res.Configuration; import android.database.sqlite.SQLiteDatabase; import android.graphics.Bitmap; +import android.graphics.Color; import android.graphics.Rect; import android.graphics.RectF; import android.os.Build; @@ -1275,11 +1276,19 @@ public class Launcher extends StatefulActivity // Setup the drag controller (drop targets have to be added in reverse order in priority) mDropTargetBar.setup(mDragController); mAllAppsController.setupViews(mScrimView, mAppsView); + + if (SHOW_DOT_PAGINATION.get()) { + mWorkspace.getPageIndicator().setShouldAutoHide(true); + mWorkspace.getPageIndicator().setPaintColor( + Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText) + ? Color.BLACK + : Color.WHITE); + } } @Override public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { - if ((SHOW_DOT_PAGINATION.get()) && WorkspacePageIndicator.class.getName().equals(name)) { + if (SHOW_DOT_PAGINATION.get() && WorkspacePageIndicator.class.getName().equals(name)) { return LayoutInflater.from(context).inflate(R.layout.page_indicator_dots, (ViewGroup) parent, false); } diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java index 141388f3c5..b89c71513b 100644 --- a/src/com/android/launcher3/folder/FolderPagedView.java +++ b/src/com/android/launcher3/folder/FolderPagedView.java @@ -113,7 +113,6 @@ public class FolderPagedView extends PagedView implements Cli public void setFolder(Folder folder) { mFolder = folder; mPageIndicator = folder.findViewById(R.id.folder_page_indicator); - mPageIndicator.setShouldAutoHide(false); initParentViews(folder); } diff --git a/src/com/android/launcher3/pageindicators/PageIndicator.java b/src/com/android/launcher3/pageindicators/PageIndicator.java index ec691931a1..570d6ff267 100644 --- a/src/com/android/launcher3/pageindicators/PageIndicator.java +++ b/src/com/android/launcher3/pageindicators/PageIndicator.java @@ -46,4 +46,11 @@ public interface PageIndicator { default void skipAnimationsToEnd() { // No-op by default } + + /** + * Sets the paint color. + */ + default void setPaintColor(int color) { + // No-op by default + } } diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java index c324ce3cf5..b2c64b3a0d 100644 --- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java +++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java @@ -35,6 +35,7 @@ import android.os.Handler; import android.os.Looper; import android.util.AttributeSet; import android.util.FloatProperty; +import android.util.IntProperty; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewOutlineProvider; @@ -58,7 +59,8 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator private static final float SHIFT_THRESHOLD = 0.1f; private static final long ANIMATION_DURATION = 150; private static final int PAGINATION_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay(); - private static final int ALPHA_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration(); + private static final int PAGINATION_FADE_IN_DURATION = 83; + private static final int PAGINATION_FADE_OUT_DURATION = 167; private static final int ENTER_ANIMATION_START_DELAY = 300; private static final int ENTER_ANIMATION_STAGGERED_DELAY = 150; @@ -66,8 +68,9 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator private static final int PAGE_INDICATOR_ALPHA = 255; private static final int DOT_ALPHA = 128; - private static final int DOT_GAP_FACTOR = 3; - private static final int VISIBLE_ALPHA = 1; + private static final float DOT_ALPHA_FRACTION = 0.5f; + private static final int DOT_GAP_FACTOR = SHOW_DOT_PAGINATION.get() ? 4 : 3; + private static final int VISIBLE_ALPHA = 255; private static final int INVISIBLE_ALPHA = 0; private Paint mPaginationPaint; @@ -89,21 +92,21 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator obj.invalidate(); obj.invalidateOutline(); } - }; + }; - private static final FloatProperty PAGINATION_ALPHA = - new FloatProperty("pagination_alpha") { - @Override - public Float get(PageIndicatorDots obj) { - return obj.getAlpha(); - } + private static final IntProperty PAGINATION_ALPHA = + new IntProperty("pagination_alpha") { + @Override + public Integer get(PageIndicatorDots obj) { + return obj.mPaginationPaint.getAlpha(); + } - @Override - public void setValue(PageIndicatorDots obj, float alpha) { - obj.setAlpha(alpha); - obj.invalidate(); - } - }; + @Override + public void setValue(PageIndicatorDots obj, int alpha) { + obj.mPaginationPaint.setAlpha(alpha); + obj.invalidate(); + } + }; private final Handler mDelayedPaginationFadeHandler = new Handler(Looper.getMainLooper()); private final float mDotRadius; @@ -112,9 +115,8 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator private int mNumPages; private int mActivePage; - private int mCurrentScroll; private int mTotalScroll; - private boolean mShouldAutoHide = true; + private boolean mShouldAutoHide; private int mToAlpha; /** @@ -133,7 +135,8 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator private float[] mEntryAnimationRadiusFactors; - private Runnable mHidePaginationRunnable = () -> animatePaginationToAlpha(INVISIBLE_ALPHA); + private final Runnable mHidePaginationRunnable = + () -> animatePaginationToAlpha(INVISIBLE_ALPHA); public PageIndicatorDots(Context context) { this(context, null); @@ -149,7 +152,10 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator mPaginationPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaginationPaint.setStyle(Style.FILL); mPaginationPaint.setColor(Themes.getAttrColor(context, R.attr.folderPaginationColor)); - mDotRadius = getResources().getDimension(R.dimen.page_indicator_dot_size) / 2; + mDotRadius = (SHOW_DOT_PAGINATION.get() + ? getResources().getDimension(R.dimen.page_indicator_dot_size_v2) + : getResources().getDimension(R.dimen.page_indicator_dot_size)) + / 2; mCircleGap = DOT_GAP_FACTOR * mDotRadius; setOutlineProvider(new MyOutlineProver()); mIsRtl = Utilities.isRtl(getResources()); @@ -157,15 +163,19 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator @Override public void setScroll(int currentScroll, int totalScroll) { - if (SHOW_DOT_PAGINATION.get()) { - animatePaginationToAlpha(VISIBLE_ALPHA); + if (SHOW_DOT_PAGINATION.get() && mActivePage != 0 && currentScroll == 0) { + CURRENT_POSITION.set(this, (float) mActivePage); + return; } if (mNumPages <= 1) { - mCurrentScroll = 0; return; } + if (mShouldAutoHide) { + animatePaginationToAlpha(VISIBLE_ALPHA); + } + if (mIsRtl) { currentScroll = totalScroll - currentScroll; } @@ -173,7 +183,7 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator mTotalScroll = totalScroll; int scrollPerPage = totalScroll / (mNumPages - 1); - int pageToLeft = currentScroll / scrollPerPage; + int pageToLeft = scrollPerPage == 0 ? 0 : currentScroll / scrollPerPage; int pageToLeftScroll = pageToLeft * scrollPerPage; int pageToRightScroll = pageToLeftScroll + scrollPerPage; @@ -181,31 +191,39 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator if (currentScroll < pageToLeftScroll + scrollThreshold) { // scroll is within the left page's threshold animateToPosition(pageToLeft); - if (SHOW_DOT_PAGINATION.get()) { + if (mShouldAutoHide) { hideAfterDelay(); } } else if (currentScroll > pageToRightScroll - scrollThreshold) { // scroll is far enough from left page to go to the right page animateToPosition(pageToLeft + 1); - if (SHOW_DOT_PAGINATION.get()) { + if (mShouldAutoHide) { hideAfterDelay(); } } else { // scroll is between left and right page animateToPosition(pageToLeft + SHIFT_PER_ANIMATION); + if (mShouldAutoHide) { + mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null); + } } } @Override public void setShouldAutoHide(boolean shouldAutoHide) { - mShouldAutoHide = shouldAutoHide; - if (shouldAutoHide && this.getAlpha() > INVISIBLE_ALPHA) { + mShouldAutoHide = shouldAutoHide && SHOW_DOT_PAGINATION.get(); + if (shouldAutoHide && mPaginationPaint.getAlpha() > INVISIBLE_ALPHA) { hideAfterDelay(); } else if (!shouldAutoHide) { mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null); } } + @Override + public void setPaintColor(int color) { + mPaginationPaint.setColor(color); + } + private void hideAfterDelay() { mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null); mDelayedPaginationFadeHandler.postDelayed(mHidePaginationRunnable, PAGINATION_FADE_DELAY); @@ -216,14 +234,17 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator // Ignore the new animation if it is going to the same alpha as the current animation. return; } - mToAlpha = alpha; if (mAlphaAnimator != null) { mAlphaAnimator.cancel(); } - mAlphaAnimator = ObjectAnimator.ofFloat(this, PAGINATION_ALPHA, + mAlphaAnimator = ObjectAnimator.ofInt(this, PAGINATION_ALPHA, alpha); - mAlphaAnimator.setDuration(ALPHA_ANIMATE_DURATION); + // If we are animating to decrease the alpha, then it's a fade out animation + // whereas if we are animating to increase the alpha, it's a fade in animation. + mAlphaAnimator.setDuration(alpha < mToAlpha + ? PAGINATION_FADE_OUT_DURATION + : PAGINATION_FADE_IN_DURATION); mAlphaAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -231,7 +252,7 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator } }); mAlphaAnimator.start(); - + mToAlpha = alpha; } /** @@ -349,7 +370,12 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator @Override protected void onDraw(Canvas canvas) { - if ((mShouldAutoHide && mTotalScroll == 0) || mNumPages < 2) { + if (mNumPages < 2) { + return; + } + + if (mShouldAutoHide && mTotalScroll == 0) { + mPaginationPaint.setAlpha(INVISIBLE_ALPHA); return; } @@ -373,15 +399,19 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator x += circleGap; } } else { + int alpha = mPaginationPaint.getAlpha(); + // Here we draw the dots - mPaginationPaint.setAlpha(DOT_ALPHA); + mPaginationPaint.setAlpha(SHOW_DOT_PAGINATION.get() + ? ((int) (alpha * DOT_ALPHA_FRACTION)) + : DOT_ALPHA); for (int i = 0; i < mNumPages; i++) { canvas.drawCircle(x, y, mDotRadius, mPaginationPaint); x += circleGap; } // Here we draw the current page indicator - mPaginationPaint.setAlpha(PAGE_INDICATOR_ALPHA); + mPaginationPaint.setAlpha(SHOW_DOT_PAGINATION.get() ? alpha : PAGE_INDICATOR_ALPHA); canvas.drawRoundRect(getActiveRect(), mDotRadius, mDotRadius, mPaginationPaint); } } @@ -450,6 +480,9 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator @Override public void onAnimationEnd(Animator animation) { if (!mCancelled) { + if (mShouldAutoHide && SHOW_DOT_PAGINATION.get()) { + hideAfterDelay(); + } mAnimator = null; animateToPosition(mFinalPosition); } From 877088e6c3a5175d67d5b44af0cf10ddd2d25045 Mon Sep 17 00:00:00 2001 From: Sebastian Franco Date: Tue, 3 Jan 2023 15:16:22 -0700 Subject: [PATCH 040/469] Change the access of cellX/Y of CellLayoutLayoutParams to private Bug: 188081026 Test: no op change, should compile Test: ReorderWidgets Change-Id: I20367974e5a4cead406e18eb66dafd4d59651b2a Merged-In: I20367974e5a4cead406e18eb66dafd4d59651b2a --- .../util/StaggeredWorkspaceAnim.java | 4 +- .../launcher3/AppWidgetResizeFrame.java | 14 +-- src/com/android/launcher3/CellLayout.java | 58 +++++----- .../launcher3/ShortcutAndWidgetContainer.java | 6 +- src/com/android/launcher3/Workspace.java | 18 ++-- .../launcher3/WorkspaceLayoutManager.java | 7 +- .../LauncherAccessibilityDelegate.java | 4 +- .../celllayout/CellLayoutLayoutParams.java | 100 ++++++++++++------ .../android/launcher3/folder/FolderIcon.java | 2 +- .../launcher3/folder/FolderPagedView.java | 4 +- .../launcher3/celllayout/ReorderWidgets.java | 4 +- 11 files changed, 136 insertions(+), 85 deletions(-) diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java index ad54a709d7..cd5edab9d6 100644 --- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java +++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java @@ -124,7 +124,7 @@ public class StaggeredWorkspaceAnim { for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) { View child = hotseatIcons.getChildAt(i); CellLayoutLayoutParams lp = ((CellLayoutLayoutParams) child.getLayoutParams()); - addStaggeredAnimationForView(child, lp.cellY + 1, totalRows, duration); + addStaggeredAnimationForView(child, lp.getCellY() + 1, totalRows, duration); } } else { final int hotseatRow, qsbRow; @@ -194,7 +194,7 @@ public class StaggeredWorkspaceAnim { for (int i = itemsContainer.getChildCount() - 1; i >= 0; i--) { View child = itemsContainer.getChildAt(i); CellLayoutLayoutParams lp = ((CellLayoutLayoutParams) child.getLayoutParams()); - addStaggeredAnimationForView(child, lp.cellY + lp.cellVSpan, totalRows, duration); + addStaggeredAnimationForView(child, lp.getCellY() + lp.cellVSpan, totalRows, duration); } mAnimators.addListener(new AnimatorListenerAdapter() { diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java index 76a91c0029..85bd2d3b7b 100644 --- a/src/com/android/launcher3/AppWidgetResizeFrame.java +++ b/src/com/android/launcher3/AppWidgetResizeFrame.java @@ -268,8 +268,10 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O CellLayoutLayoutParams lp = (CellLayoutLayoutParams) mWidgetView.getLayoutParams(); ItemInfo widgetInfo = (ItemInfo) mWidgetView.getTag(); - lp.cellX = lp.tmpCellX = widgetInfo.cellX; - lp.cellY = lp.tmpCellY = widgetInfo.cellY; + lp.setCellX(widgetInfo.cellX); + lp.setTmpCellX(widgetInfo.cellX); + lp.setCellY(widgetInfo.cellY); + lp.setTmpCellY(widgetInfo.cellY); lp.cellHSpan = widgetInfo.spanX; lp.cellVSpan = widgetInfo.spanY; lp.isLockedToGrid = true; @@ -425,8 +427,8 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O int spanX = lp.cellHSpan; int spanY = lp.cellVSpan; - int cellX = lp.useTmpCoords ? lp.tmpCellX : lp.cellX; - int cellY = lp.useTmpCoords ? lp.tmpCellY : lp.cellY; + int cellX = lp.useTmpCoords ? lp.getTmpCellX() : lp.getCellX(); + int cellY = lp.useTmpCoords ? lp.getTmpCellY() : lp.getCellY(); // For each border, we bound the resizing based on the minimum width, and the maximum // expandability. @@ -467,8 +469,8 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O mLauncher.getString(R.string.widget_resized, spanX, spanY)); } - lp.tmpCellX = cellX; - lp.tmpCellY = cellY; + lp.setTmpCellX(cellX); + lp.setTmpCellY(cellY); lp.cellHSpan = spanX; lp.cellVSpan = spanY; mRunningVInc += vSpanDelta; diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index ecfd2307c5..05b225c6e4 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -623,8 +623,8 @@ public class CellLayout extends ViewGroup { if (alpha <= 0) continue; mVisualizeGridPaint.setAlpha(255); - int x = mDragOutlines[i].cellX; - int y = mDragOutlines[i].cellY; + int x = mDragOutlines[i].getCellX(); + int y = mDragOutlines[i].getCellY(); int spanX = mDragOutlines[i].cellHSpan; int spanY = mDragOutlines[i].cellVSpan; @@ -764,7 +764,8 @@ public class CellLayout extends ViewGroup { // Generate an id for each view, this assumes we have at most 256x256 cells // per workspace screen - if (lp.cellX >= 0 && lp.cellX <= mCountX - 1 && lp.cellY >= 0 && lp.cellY <= mCountY - 1) { + if (lp.getCellX() >= 0 && lp.getCellX() <= mCountX - 1 + && lp.getCellY() >= 0 && lp.getCellY() <= mCountY - 1) { // If the horizontal or vertical span is set to -1, it is taken to // mean that it spans the extent of the CellLayout if (lp.cellHSpan < 0) lp.cellHSpan = mCountX; @@ -1072,7 +1073,7 @@ public class CellLayout extends ViewGroup { if (adjustOccupied) { GridOccupancy occupied = permanent ? mOccupied : mTmpOccupied; - occupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false); + occupied.markCells(lp.getCellX(), lp.getCellY(), lp.cellHSpan, lp.cellVSpan, false); occupied.markCells(cellX, cellY, lp.cellHSpan, lp.cellVSpan, true); } @@ -1083,11 +1084,11 @@ public class CellLayout extends ViewGroup { final int oldY = lp.y; lp.isLockedToGrid = true; if (permanent) { - lp.cellX = info.cellX = cellX; - lp.cellY = info.cellY = cellY; + lp.setCellX(info.cellX = cellX); + lp.setCellY(info.cellY = cellY); } else { - lp.tmpCellX = cellX; - lp.tmpCellY = cellY; + lp.setTmpCellX(cellX); + lp.setTmpCellY(cellY); } clc.setupLp(child); final int newX = lp.x; @@ -1167,8 +1168,8 @@ public class CellLayout extends ViewGroup { mDragOutlineCurrent = (oldIndex + 1) % mDragOutlines.length; CellLayoutLayoutParams cell = mDragOutlines[mDragOutlineCurrent]; - cell.cellX = cellX; - cell.cellY = cellY; + cell.setCellX(cellX); + cell.setCellY(cellY); cell.cellHSpan = spanX; cell.cellVSpan = spanY; @@ -1385,8 +1386,8 @@ public class CellLayout extends ViewGroup { CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams(); CellAndSpan c = solution.map.get(child); if (c != null) { - lp.tmpCellX = c.cellX; - lp.tmpCellY = c.cellY; + lp.setTmpCellX(c.cellX); + lp.setTmpCellY(c.cellY); lp.cellHSpan = c.spanX; lp.cellVSpan = c.spanY; mTmpOccupied.markCells(c, true); @@ -1433,7 +1434,7 @@ public class CellLayout extends ViewGroup { CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams(); if (c != null && !skip && (child instanceof Reorderable)) { ReorderPreviewAnimation rha = new ReorderPreviewAnimation((Reorderable) child, - mode, lp.cellX, lp.cellY, c.cellX, c.cellY, c.spanX, c.spanY); + mode, lp.getCellX(), lp.getCellY(), c.cellX, c.cellY, c.spanX, c.spanY); rha.animate(); } } @@ -1626,12 +1627,14 @@ public class CellLayout extends ViewGroup { // We do a null check here because the item info can be null in the case of the // AllApps button in the hotseat. if (info != null && child != dragView) { - final boolean requiresDbUpdate = (info.cellX != lp.tmpCellX - || info.cellY != lp.tmpCellY || info.spanX != lp.cellHSpan + final boolean requiresDbUpdate = (info.cellX != lp.getTmpCellX() + || info.cellY != lp.getTmpCellY() || info.spanX != lp.cellHSpan || info.spanY != lp.cellVSpan); - info.cellX = lp.cellX = lp.tmpCellX; - info.cellY = lp.cellY = lp.tmpCellY; + lp.setCellX(lp.getTmpCellX()); + info.cellX = lp.getTmpCellX(); + info.cellY = lp.getTmpCellY(); + lp.setCellY(lp.getTmpCellY()); info.spanX = lp.cellHSpan; info.spanY = lp.cellVSpan; @@ -1697,7 +1700,8 @@ public class CellLayout extends ViewGroup { if (child == dragView) continue; CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams(); - r1.set(lp.cellX, lp.cellY, lp.cellX + lp.cellHSpan, lp.cellY + lp.cellVSpan); + r1.set(lp.getCellX(), lp.getCellY(), lp.getCellX() + lp.cellHSpan, + lp.getCellY() + lp.cellVSpan); if (Rect.intersects(r0, r1)) { mIntersectingViews.add(child); if (boundingRect != null) { @@ -1723,11 +1727,11 @@ public class CellLayout extends ViewGroup { View child = mShortcutsAndWidgets.getChildAt(i); CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams(); - if (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY) { - lp.tmpCellX = lp.cellX; - lp.tmpCellY = lp.cellY; - animateChildToPosition(child, lp.cellX, lp.cellY, REORDER_ANIMATION_DURATION, - 0, false, false); + if (lp.getTmpCellX() != lp.getCellX() || lp.getTmpCellY() != lp.getCellY()) { + lp.setTmpCellX(lp.getCellX()); + lp.setTmpCellY(lp.getCellY()); + animateChildToPosition(child, lp.getCellX(), lp.getCellY(), + REORDER_ANIMATION_DURATION, 0, false, false); } } setItemPlacementDirty(false); @@ -2449,9 +2453,9 @@ public class CellLayout extends ViewGroup { CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams(); CellAndSpan c; if (temp) { - c = new CellAndSpan(lp.tmpCellX, lp.tmpCellY, lp.cellHSpan, lp.cellVSpan); + c = new CellAndSpan(lp.getTmpCellX(), lp.getTmpCellY(), lp.cellHSpan, lp.cellVSpan); } else { - c = new CellAndSpan(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan); + c = new CellAndSpan(lp.getCellX(), lp.getCellY(), lp.cellHSpan, lp.cellVSpan); } solution.add(child, c); } @@ -2794,7 +2798,7 @@ public class CellLayout extends ViewGroup { if (view == null || view.getParent() != mShortcutsAndWidgets) return; CellLayoutLayoutParams lp = (CellLayoutLayoutParams) view.getLayoutParams(); - mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true); + mOccupied.markCells(lp.getCellX(), lp.getCellY(), lp.cellHSpan, lp.cellVSpan, true); } public void markCellsAsUnoccupiedForView(View view) { @@ -2807,7 +2811,7 @@ public class CellLayout extends ViewGroup { if (view == null || view.getParent() != mShortcutsAndWidgets) return; CellLayoutLayoutParams lp = (CellLayoutLayoutParams) view.getLayoutParams(); - mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false); + mOccupied.markCells(lp.getCellX(), lp.getCellY(), lp.cellHSpan, lp.cellVSpan, false); } public int getDesiredWidth() { diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java index 7a74d7ed1b..5f39f7e483 100644 --- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java +++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java @@ -83,8 +83,8 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon. View child = getChildAt(i); CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams(); - if ((lp.cellX <= cellX) && (cellX < lp.cellX + lp.cellHSpan) - && (lp.cellY <= cellY) && (cellY < lp.cellY + lp.cellVSpan)) { + if ((lp.getCellX() <= cellX) && (cellX < lp.getCellX() + lp.cellHSpan) + && (lp.getCellY() <= cellY) && (cellY < lp.getCellY() + lp.cellVSpan)) { return child; } } @@ -260,7 +260,7 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon. lp.canReorder = false; if (mContainerType == HOTSEAT) { CellLayout cl = (CellLayout) getParent(); - cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY); + cl.setFolderLeaveBehindCell(lp.getCellX(), lp.getCellY()); } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 2b9c1359b7..460c65866b 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1757,7 +1757,8 @@ public class Workspace extends PagedView boolean willCreateUserFolder(ItemInfo info, View dropOverView, boolean considerTimeout) { if (dropOverView != null) { CellLayoutLayoutParams lp = (CellLayoutLayoutParams) dropOverView.getLayoutParams(); - if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY)) { + if (lp.useTmpCoords && (lp.getTmpCellX() != lp.getCellX() + || lp.getTmpCellY() != lp.getCellY())) { return false; } } @@ -1792,7 +1793,8 @@ public class Workspace extends PagedView boolean willAddToExistingUserFolder(ItemInfo dragInfo, View dropOverView) { if (dropOverView != null) { CellLayoutLayoutParams lp = (CellLayoutLayoutParams) dropOverView.getLayoutParams(); - if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY)) { + if (lp.useTmpCoords && (lp.getTmpCellX() != lp.getCellX() + || lp.getTmpCellY() != lp.getCellY())) { return false; } } @@ -2009,8 +2011,10 @@ public class Workspace extends PagedView // update the item's position after drop CellLayoutLayoutParams lp = (CellLayoutLayoutParams) cell.getLayoutParams(); - lp.cellX = lp.tmpCellX = mTargetCell[0]; - lp.cellY = lp.tmpCellY = mTargetCell[1]; + lp.setTmpCellX(mTargetCell[0]); + lp.setCellX(mTargetCell[0]); + lp.setTmpCellY(mTargetCell[1]); + lp.setCellY(mTargetCell[1]); lp.cellHSpan = item.spanX; lp.cellVSpan = item.spanY; lp.isLockedToGrid = true; @@ -2024,7 +2028,7 @@ public class Workspace extends PagedView (LauncherAppWidgetHostView) cell, dropTargetLayout); } mLauncher.getModelWriter().modifyItemInDatabase(info, container, screenId, - lp.cellX, lp.cellY, item.spanX, item.spanY); + lp.getCellX(), lp.getCellY(), item.spanX, item.spanY); } else { if (!returnToOriginalCellToPreventShuffling) { onNoCellFound(dropTargetLayout, d.dragInfo, d.logInstanceId); @@ -2035,8 +2039,8 @@ public class Workspace extends PagedView // If we can't find a drop location, we return the item to its original position CellLayoutLayoutParams lp = (CellLayoutLayoutParams) cell.getLayoutParams(); - mTargetCell[0] = lp.cellX; - mTargetCell[1] = lp.cellY; + mTargetCell[0] = lp.getCellX(); + mTargetCell[1] = lp.getCellY(); CellLayout layout = (CellLayout) cell.getParent().getParent(); layout.markCellsAsOccupiedForView(cell); } diff --git a/src/com/android/launcher3/WorkspaceLayoutManager.java b/src/com/android/launcher3/WorkspaceLayoutManager.java index 91e12faa1d..bf448c96c4 100644 --- a/src/com/android/launcher3/WorkspaceLayoutManager.java +++ b/src/com/android/launcher3/WorkspaceLayoutManager.java @@ -117,8 +117,8 @@ public interface WorkspaceLayoutManager { lp = new CellLayoutLayoutParams(x, y, spanX, spanY, screenId); } else { lp = (CellLayoutLayoutParams) genericLp; - lp.cellX = x; - lp.cellY = y; + lp.setCellX(x); + lp.setCellY(y); lp.cellHSpan = spanX; lp.cellVSpan = spanY; } @@ -136,7 +136,8 @@ public interface WorkspaceLayoutManager { // TODO: This branch occurs when the workspace is adding views // outside of the defined grid // maybe we should be deleting these items from the LauncherModel? - Log.e(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout"); + Log.e(TAG, "Failed to add to item at (" + lp.getCellX() + "," + lp.getCellY() + + ") to CellLayout"); } child.setHapticFeedbackEnabled(false); diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index 063b82e0d9..3c316b831b 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -259,7 +259,7 @@ public class LauncherAccessibilityDelegate extends BaseAccessibilityDelegate implements Cli textView.setLayoutParams(new CellLayoutLayoutParams( item.cellX, item.cellY, item.spanX, item.spanY, item.screenId)); } else { - lp.cellX = item.cellX; - lp.cellY = item.cellY; + lp.setCellX(item.cellX); + lp.setCellY(item.cellY); lp.cellHSpan = lp.cellVSpan = 1; } return textView; diff --git a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java index 843f0112ba..7b38ed6900 100644 --- a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java +++ b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java @@ -132,10 +132,10 @@ public class ReorderWidgets extends AbstractLauncherUiTest { (CellLayoutLayoutParams) callView.getLayoutParams(); // is icon if (callView instanceof DoubleShadowBubbleTextView) { - board.addIcon(params.cellX, params.cellY); + board.addIcon(params.getCellX(), params.getCellY()); } else { // is widget - board.addWidget(params.cellX, params.cellY, params.cellHSpan, + board.addWidget(params.getCellX(), params.getCellY(), params.cellHSpan, params.cellVSpan, (char) ('A' + widgetCount)); widgetCount++; } From 4eb502ae10b94c8d6d63b1bfd51632f680b0fe89 Mon Sep 17 00:00:00 2001 From: Federico Baron Date: Wed, 14 Dec 2022 00:04:53 -0800 Subject: [PATCH 041/469] Add finish icon scale animation for downloading apps We add an animation when we finish downloading an app where the scale of the icon increases to from scale 0.867 to scale 1 Bug: 254858049 Test: Download an app and visualize finish download animation Change-Id: I67912f86b0e35091a5fe1b39c3463a1ff203bd3b --- src/com/android/launcher3/BubbleTextView.java | 33 +-- .../graphics/PreloadIconDrawable.java | 218 ++++++++---------- .../util/LauncherBindableItemsContainer.java | 9 +- 3 files changed, 121 insertions(+), 139 deletions(-) diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 9f54f09bb6..edbce1068e 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -16,11 +16,14 @@ package com.android.launcher3; +import static com.android.launcher3.config.FeatureFlags.ENABLE_DOWNLOAD_APP_UX_V2; import static com.android.launcher3.config.FeatureFlags.ENABLE_ICON_LABEL_AUTO_SCALING; import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon; import static com.android.launcher3.icons.BitmapInfo.FLAG_NO_BADGE; import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED; import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; +import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE; +import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -291,7 +294,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, @UiThread public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean animate, int staggerIndex) { - applyFromWorkspaceItem(info, false); + applyFromWorkspaceItem(info, null); } /** @@ -320,10 +323,10 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, } @UiThread - public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean promiseStateChanged) { + public void applyFromWorkspaceItem(WorkspaceItemInfo info, PreloadIconDrawable icon) { applyIconAndLabel(info); setItemInfo(info); - applyLoadingState(promiseStateChanged); + applyLoadingState(icon); applyDotState(info, false /* animate */); setDownloadStateContentDescription(info, info.getProgressLevel()); } @@ -710,23 +713,23 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, * If this app is installed and downloading incrementally, the progress bar will be updated * with the total download progress. */ - public void applyLoadingState(boolean promiseStateChanged) { + public void applyLoadingState(PreloadIconDrawable icon) { if (getTag() instanceof ItemInfoWithIcon) { WorkspaceItemInfo info = (WorkspaceItemInfo) getTag(); - if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) - != 0) { - updateProgressBarUi(info.getProgressLevel() == 100); - } else if (info.hasPromiseIconUi() || (info.runtimeStatusFlags - & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) { - updateProgressBarUi(promiseStateChanged); + if ((info.runtimeStatusFlags & FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0 + || info.hasPromiseIconUi() + || (info.runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) != 0 + || (ENABLE_DOWNLOAD_APP_UX_V2.get() && icon != null)) { + updateProgressBarUi(icon); } } } - private void updateProgressBarUi(boolean maybePerformFinishedAnimation) { + private void updateProgressBarUi(PreloadIconDrawable oldIcon) { + FastBitmapDrawable originalIcon = mIcon; PreloadIconDrawable preloadDrawable = applyProgressLevel(); - if (preloadDrawable != null && maybePerformFinishedAnimation) { - preloadDrawable.maybePerformFinishedAnimation(); + if (preloadDrawable != null && oldIcon != null) { + preloadDrawable.maybePerformFinishedAnimation(oldIcon, () -> setIcon(originalIcon)); } } @@ -824,12 +827,12 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, != 0) { String percentageString = NumberFormat.getPercentInstance() .format(progressLevel * 0.01); - if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) { + if ((info.runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) != 0) { setContentDescription(getContext() .getString( R.string.app_installing_title, info.title, percentageString)); } else if ((info.runtimeStatusFlags - & ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0) { + & FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0) { setContentDescription(getContext() .getString( R.string.app_downloading_title, info.title, percentageString)); diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java index de47cb5057..9001a5270e 100644 --- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java +++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java @@ -28,6 +28,7 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.ColorFilter; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; @@ -41,6 +42,7 @@ import android.util.Property; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatedFloat; +import com.android.launcher3.anim.AnimatorListeners; import com.android.launcher3.icons.FastBitmapDrawable; import com.android.launcher3.icons.GraphicsUtils; import com.android.launcher3.model.data.ItemInfoWithIcon; @@ -53,7 +55,7 @@ import java.util.function.Function; /** * Extension of {@link FastBitmapDrawable} which shows a progress bar around the icon. */ -public class PreloadIconDrawable extends FastBitmapDrawable implements Runnable { +public class PreloadIconDrawable extends FastBitmapDrawable { private static final Property INTERNAL_STATE = new Property(Float.TYPE, "internalStateProgress") { @@ -78,16 +80,19 @@ public class PreloadIconDrawable extends FastBitmapDrawable implements Runnable // The smaller the number, the faster the animation would be. // Duration = COMPLETE_ANIM_FRACTION * DURATION_SCALE - private static final float COMPLETE_ANIM_FRACTION = 0.3f; + private static final float COMPLETE_ANIM_FRACTION = 1f; private static final float SMALL_SCALE = ENABLE_DOWNLOAD_APP_UX_V2.get() ? 0.867f : 0.7f; - private static final float PROGRESS_STROKE_SCALE = 0.075f; + private static final float PROGRESS_STROKE_SCALE = ENABLE_DOWNLOAD_APP_UX_V2.get() + ? 0.0655f + : 0.075f; + private static final float PROGRESS_BOUNDS_SCALE = 0.075f; private static final int PRELOAD_ACCENT_COLOR_INDEX = 0; private static final int PRELOAD_BACKGROUND_COLOR_INDEX = 1; private static final int ALPHA_DURATION_MILLIS = 3000; - private static final float OVERLAY_ALPHA_RANGE = 127.5f; + private static final int OVERLAY_ALPHA_RANGE = 127; private static final long WAVE_MOTION_DELAY_FACTOR_MILLIS = 100; private static final WeakHashMap COLOR_FILTER_MAP = new WeakHashMap<>(); @@ -111,19 +116,17 @@ public class PreloadIconDrawable extends FastBitmapDrawable implements Runnable private final int mSystemBackgroundColor; private final boolean mIsDarkMode; - private int mTrackAlpha; private float mTrackLength; private boolean mRanFinishAnimation; - private final int mRefreshRateMillis; - private final AnimatedFloat mIconScale = new AnimatedFloat(this::invalidateSelf); - private final AnimatedFloat mOverlayAlpha = new AnimatedFloat(this::updateOverlayAlpha); - private boolean mShouldAnimateScaleAndAlpha; // Progress of the internal state. [0, 1] indicates the fraction of completed progress, // [1, (1 + COMPLETE_ANIM_FRACTION)] indicates the progress of zoom animation. private float mInternalStateProgress; + // This multiplier is used to animate scale when going from 0 to non-zero and expanding + private final Runnable mInvalidateRunnable = this::invalidateSelf; + private final AnimatedFloat mIconScaleMultiplier = new AnimatedFloat(mInvalidateRunnable); private ObjectAnimator mCurrentAnim; @@ -160,10 +163,7 @@ public class PreloadIconDrawable extends FastBitmapDrawable implements Runnable mRefreshRateMillis = refreshRateMillis; // If it's a pending app we will animate scale and alpha when it's no longer pending. - if (ENABLE_DOWNLOAD_APP_UX_V2.get() && info.getProgressLevel() == 0) { - mShouldAnimateScaleAndAlpha = true; - mOverlayAlpha.updateValue(127); - } + mIconScaleMultiplier.updateValue(info.getProgressLevel() == 0 ? 0 : 1); setLevel(info.getProgressLevel()); setIsStartable(info.isAppStartable()); @@ -173,14 +173,17 @@ public class PreloadIconDrawable extends FastBitmapDrawable implements Runnable protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); - float progressWidth = PROGRESS_STROKE_SCALE * bounds.width(); + + float progressWidth = bounds.width() * (ENABLE_DOWNLOAD_APP_UX_V2.get() + ? PROGRESS_BOUNDS_SCALE + : PROGRESS_STROKE_SCALE); mTmpMatrix.setScale( (bounds.width() - 2 * progressWidth) / DEFAULT_PATH_SIZE, (bounds.height() - 2 * progressWidth) / DEFAULT_PATH_SIZE); mTmpMatrix.postTranslate(bounds.left + progressWidth, bounds.top + progressWidth); mShapePath.transform(mTmpMatrix, mScaledTrackPath); - mProgressPaint.setStrokeWidth(progressWidth); + mProgressPaint.setStrokeWidth(PROGRESS_STROKE_SCALE * bounds.width()); mPathMeasure.setPath(mScaledTrackPath, true); mTrackLength = mPathMeasure.getLength(); @@ -195,26 +198,35 @@ public class PreloadIconDrawable extends FastBitmapDrawable implements Runnable return; } - // Draw background. - mProgressPaint.setStyle(Paint.Style.FILL_AND_STROKE); - mProgressPaint.setColor(mSystemBackgroundColor); - canvas.drawPath(mScaledTrackPath, mProgressPaint); + if (!ENABLE_DOWNLOAD_APP_UX_V2.get() && mInternalStateProgress > 0) { + // Draw background. + mProgressPaint.setStyle(Paint.Style.FILL_AND_STROKE); + mProgressPaint.setColor(mSystemBackgroundColor); + canvas.drawPath(mScaledTrackPath, mProgressPaint); + } - // Draw track and progress. - mProgressPaint.setStyle(Paint.Style.STROKE); - mProgressPaint.setColor(mIsStartable ? mIndicatorColor : mSystemAccentColor); - mProgressPaint.setAlpha(TRACK_ALPHA); - canvas.drawPath(mScaledTrackPath, mProgressPaint); - mProgressPaint.setAlpha(mTrackAlpha); - canvas.drawPath(mScaledProgressPath, mProgressPaint); + if (!ENABLE_DOWNLOAD_APP_UX_V2.get() || mInternalStateProgress > 0) { + // Draw track and progress. + mProgressPaint.setStyle(Paint.Style.STROKE); + mProgressPaint.setColor(mSystemAccentColor); + mProgressPaint.setAlpha(TRACK_ALPHA); + canvas.drawPath(mScaledTrackPath, mProgressPaint); + mProgressPaint.setAlpha(MAX_PAINT_ALPHA); + canvas.drawPath(mScaledProgressPath, mProgressPaint); + } int saveCount = canvas.save(); - canvas.scale( - mIconScale.value, mIconScale.value, bounds.exactCenterX(), bounds.exactCenterY()); + float scale = ENABLE_DOWNLOAD_APP_UX_V2.get() + ? 1 - mIconScaleMultiplier.value * (1 - SMALL_SCALE) + : SMALL_SCALE; + canvas.scale(scale, scale, bounds.exactCenterX(), bounds.exactCenterY()); + + ColorFilter filter = getOverlayFilter(); + mPaint.setColorFilter(filter); super.drawInternal(canvas, bounds); canvas.restoreToCount(saveCount); - if (ENABLE_DOWNLOAD_APP_UX_V2.get() && mInternalStateProgress == 0) { + if (ENABLE_DOWNLOAD_APP_UX_V2.get() && filter != null) { reschedule(); } } @@ -232,7 +244,7 @@ public class PreloadIconDrawable extends FastBitmapDrawable implements Runnable @Override protected boolean onLevelChange(int level) { // Run the animation if we have already been bound. - updateInternalState(level * 0.01f, getBounds().width() > 0, false); + updateInternalState(level * 0.01f, false, null); return true; } @@ -240,12 +252,18 @@ public class PreloadIconDrawable extends FastBitmapDrawable implements Runnable * Runs the finish animation if it is has not been run after last call to * {@link #onLevelChange} */ - public void maybePerformFinishedAnimation() { + public void maybePerformFinishedAnimation( + PreloadIconDrawable oldIcon, Runnable onFinishCallback) { + + if (oldIcon.mInternalStateProgress >= 1) { + mInternalStateProgress = oldIcon.mInternalStateProgress; + } + // If the drawable was recently initialized, skip the progress animation. if (mInternalStateProgress == 0) { mInternalStateProgress = 1; } - updateInternalState(1 + COMPLETE_ANIM_FRACTION, true, true); + updateInternalState(1 + COMPLETE_ANIM_FRACTION, true, onFinishCallback); } public boolean hasNotCompleted() { @@ -260,26 +278,29 @@ public class PreloadIconDrawable extends FastBitmapDrawable implements Runnable } } - private void updateInternalState(float finalProgress, boolean shouldAnimate, boolean isFinish) { + private void updateInternalState( + float finalProgress, boolean isFinish, Runnable onFinishCallback) { if (mCurrentAnim != null) { mCurrentAnim.cancel(); mCurrentAnim = null; } - if (Float.compare(finalProgress, mInternalStateProgress) == 0) { - return; - } - if (finalProgress < mInternalStateProgress) { - shouldAnimate = false; - } - if (!shouldAnimate || mRanFinishAnimation) { + boolean animateProgress = + finalProgress >= mInternalStateProgress && getBounds().width() > 0; + if (!animateProgress || mRanFinishAnimation) { setInternalProgress(finalProgress); + if (isFinish && onFinishCallback != null) { + onFinishCallback.run(); + } } else { mCurrentAnim = ObjectAnimator.ofFloat(this, INTERNAL_STATE, finalProgress); mCurrentAnim.setDuration( (long) ((finalProgress - mInternalStateProgress) * DURATION_SCALE)); mCurrentAnim.setInterpolator(LINEAR); if (isFinish) { + if (onFinishCallback != null) { + mCurrentAnim.addListener(AnimatorListeners.forEndCallback(onFinishCallback)); + } mCurrentAnim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -297,62 +318,38 @@ public class PreloadIconDrawable extends FastBitmapDrawable implements Runnable * - icon with pending motion * - progress track is not visible * - progress bar is not visible - * for progress < 1 + * for progress < 1: * - icon without pending motion * - progress track is visible * - progress bar is visible. Progress bar is drawn as a fraction of * {@link #mScaledTrackPath}. * @see PathMeasure#getSegment(float, float, Path, boolean) - * for 1 <= progress < (1 + COMPLETE_ANIM_FRACTION) - * - we calculate fraction of progress in the above range - * - progress track is drawn with alpha based on fraction - * - progress bar is drawn at 100% with alpha based on fraction - * - icon is scaled up based on fraction and is drawn in enabled state - * for progress >= (1 + COMPLETE_ANIM_FRACTION) - * - only icon is drawn in normal state + * for progress > 1: + * - scale the icon back to full size */ private void setInternalProgress(float progress) { // Animate scale and alpha from pending to downloading state. - if (ENABLE_DOWNLOAD_APP_UX_V2.get() - && mShouldAnimateScaleAndAlpha && mInternalStateProgress == 0 && progress > 0) { - Animator iconScaleAnimator = mIconScale.animateToValue(SMALL_SCALE); + if (ENABLE_DOWNLOAD_APP_UX_V2.get() && progress > 0 && mInternalStateProgress == 0) { + // Progress is changing for the first time, animate the icon scale + Animator iconScaleAnimator = mIconScaleMultiplier.animateToValue(1); iconScaleAnimator.setDuration(SCALE_AND_ALPHA_ANIM_DURATION); iconScaleAnimator.setInterpolator(EMPHASIZED); iconScaleAnimator.start(); - - Animator overlayAlphaAnimator = mOverlayAlpha.animateToValue(0); - overlayAlphaAnimator.setDuration(SCALE_AND_ALPHA_ANIM_DURATION); - overlayAlphaAnimator.setInterpolator(EMPHASIZED); - overlayAlphaAnimator.start(); } mInternalStateProgress = progress; if (progress <= 0) { - mIconScale.updateValue(ENABLE_DOWNLOAD_APP_UX_V2.get() ? 1 : SMALL_SCALE); - mScaledTrackPath.reset(); - mTrackAlpha = MAX_PAINT_ALPHA; - } else if (progress < 1) { - mPathMeasure.getSegment(0, progress * mTrackLength, mScaledProgressPath, true); - if (ENABLE_DOWNLOAD_APP_UX_V2.get()) { - mPathMeasure.getSegment(0, mTrackLength, mScaledTrackPath, true); + if (!ENABLE_DOWNLOAD_APP_UX_V2.get()) { + mScaledTrackPath.reset(); } - - if (!ENABLE_DOWNLOAD_APP_UX_V2.get() || !mShouldAnimateScaleAndAlpha) { - mIconScale.updateValue(SMALL_SCALE); - } - mTrackAlpha = MAX_PAINT_ALPHA; + mIconScaleMultiplier.updateValue(0); } else { - setIsDisabled(mItem.isDisabled()); - mScaledTrackPath.set(mScaledProgressPath); - float fraction = (progress - 1) / COMPLETE_ANIM_FRACTION; - - if (fraction >= 1) { - // Animation has completed - mIconScale.updateValue(1); - mTrackAlpha = 0; - } else { - mTrackAlpha = Math.round((1 - fraction) * MAX_PAINT_ALPHA); - mIconScale.updateValue(SMALL_SCALE + (1 - SMALL_SCALE) * fraction); + mPathMeasure.getSegment( + 0, Math.min(progress, 1) * mTrackLength, mScaledProgressPath, true); + if (progress > 1 && ENABLE_DOWNLOAD_APP_UX_V2.get()) { + // map the scale back to original value + mIconScaleMultiplier.updateValue(Utilities.mapBoundToRange( + progress - 1, 0, COMPLETE_ANIM_FRACTION, 1, 0, EMPHASIZED)); } } invalidateSelf(); @@ -392,72 +389,49 @@ public class PreloadIconDrawable extends FastBitmapDrawable implements Runnable mRefreshRateMillis); } - @Override - public void run() { - if (!ENABLE_DOWNLOAD_APP_UX_V2.get() || mInternalStateProgress > 0) { - return; - } - if (!applyPendingIconOverlay()) { - reschedule(); - } - } - @Override public boolean setVisible(boolean visible, boolean restart) { - boolean result = super.setVisible(visible, restart); - if (visible) { - reschedule(); - } else { - unscheduleSelf(this); + if (!visible) { + unscheduleSelf(mInvalidateRunnable); } - return result; + return super.setVisible(visible, restart); } private void reschedule() { - unscheduleSelf(this); - + unscheduleSelf(mInvalidateRunnable); if (!isVisible()) { return; } - final long upTime = SystemClock.uptimeMillis(); - scheduleSelf(this, upTime - ((upTime % mRefreshRateMillis)) + mRefreshRateMillis); + scheduleSelf(mInvalidateRunnable, + upTime - ((upTime % mRefreshRateMillis)) + mRefreshRateMillis); } - /** - * Apply an overlay on the pending icon with cascading motion based on its position. - * Returns {@code true} if the icon alpha is updated, so that we re-draw. + * Returns a color filter to be used as an overlay on the pending icon with cascading motion + * based on its position. */ - private boolean applyPendingIconOverlay() { + private ColorFilter getOverlayFilter() { + if (!ENABLE_DOWNLOAD_APP_UX_V2.get() || mInternalStateProgress > 0) { + // If the download has started, we do no need to animate + return null; + } long waveMotionDelay = (mItem.cellX * WAVE_MOTION_DELAY_FACTOR_MILLIS) + (mItem.cellY * WAVE_MOTION_DELAY_FACTOR_MILLIS); long time = SystemClock.uptimeMillis(); - float newAlpha = Utilities.mapBoundToRange( - (float) (time + waveMotionDelay) % ALPHA_DURATION_MILLIS, + int alpha = (int) Utilities.mapBoundToRange( + (int) ((time + waveMotionDelay) % ALPHA_DURATION_MILLIS), 0, ALPHA_DURATION_MILLIS, 0, - MAX_PAINT_ALPHA, + OVERLAY_ALPHA_RANGE * 2, LINEAR); - if (newAlpha > OVERLAY_ALPHA_RANGE) { - newAlpha = (OVERLAY_ALPHA_RANGE - (newAlpha % OVERLAY_ALPHA_RANGE)); + if (alpha > OVERLAY_ALPHA_RANGE) { + alpha = (OVERLAY_ALPHA_RANGE - (alpha % OVERLAY_ALPHA_RANGE)); } - - boolean invalidate = false; - if ((int) mOverlayAlpha.value != newAlpha) { - mOverlayAlpha.updateValue(newAlpha); - invalidate = true; - } - return invalidate; - } - - private void updateOverlayAlpha() { int overlayColor = mIsDarkMode ? 0 : 255; - int currArgb = - Color.argb((int) mOverlayAlpha.value, overlayColor, overlayColor, overlayColor); - mPaint.setColorFilter(COLOR_FILTER_MAP.computeIfAbsent(currArgb, FILTER_FACTORY)); - invalidateSelf(); + int currArgb = Color.argb(alpha, overlayColor, overlayColor, overlayColor); + return COLOR_FILTER_MAP.computeIfAbsent(currArgb, FILTER_FACTORY); } protected static class PreloadIconConstantState extends FastBitmapConstantState { diff --git a/src/com/android/launcher3/util/LauncherBindableItemsContainer.java b/src/com/android/launcher3/util/LauncherBindableItemsContainer.java index a4cb30a374..f73940bfee 100644 --- a/src/com/android/launcher3/util/LauncherBindableItemsContainer.java +++ b/src/com/android/launcher3/util/LauncherBindableItemsContainer.java @@ -50,7 +50,12 @@ public interface LauncherBindableItemsContainer { Drawable oldIcon = shortcut.getIcon(); boolean oldPromiseState = (oldIcon instanceof PreloadIconDrawable) && ((PreloadIconDrawable) oldIcon).hasNotCompleted(); - shortcut.applyFromWorkspaceItem(si, si.isPromise() != oldPromiseState); + shortcut.applyFromWorkspaceItem( + si, + si.isPromise() != oldPromiseState + && oldIcon instanceof PreloadIconDrawable + ? (PreloadIconDrawable) oldIcon + : null); } else if (info instanceof FolderInfo && v instanceof FolderIcon) { ((FolderIcon) v).updatePreviewItems(updates::contains); } @@ -74,7 +79,7 @@ public interface LauncherBindableItemsContainer { ItemOperator op = (info, v) -> { if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView && updates.contains(info)) { - ((BubbleTextView) v).applyLoadingState(false /* promiseStateChanged */); + ((BubbleTextView) v).applyLoadingState(null); } else if (v instanceof PendingAppWidgetHostView && info instanceof LauncherAppWidgetInfo && updates.contains(info)) { From 3774824ceaafc367e06158f34fa1c99806fcde68 Mon Sep 17 00:00:00 2001 From: Fengjiang Li Date: Thu, 12 Jan 2023 10:31:24 -0800 Subject: [PATCH 042/469] Predictive swipe: show extra app icons at bottom of All Apps's RecyclerViews The maximum center scale of All Apps to Home is 90%. It means we should add 5% height to All Apps's RecyclerView to render extra app icons. Test: manual bug: b/264906511 Change-Id: I2e970580810220e25d7fc3a86c19abaf87ba2c6e --- res/values/id.xml | 2 + .../launcher3/allapps/AllAppsGridAdapter.java | 14 +++ .../allapps/AllAppsTransitionController.java | 95 ++++++++++++++++++- 3 files changed, 108 insertions(+), 3 deletions(-) diff --git a/res/values/id.xml b/res/values/id.xml index 9fc0ff8277..52a7e98f89 100644 --- a/res/values/id.xml +++ b/res/values/id.xml @@ -37,4 +37,6 @@ + + diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index 63e6d13a5c..112d47ed50 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -21,6 +21,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; +import androidx.annotation.Px; import androidx.core.view.accessibility.AccessibilityEventCompat; import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; import androidx.core.view.accessibility.AccessibilityRecordCompat; @@ -143,6 +144,19 @@ public class AllAppsGridAdapter extends cic.isSelected())); } + /** + * We need to extend all apps' RecyclerView's bottom by 5% of view height to ensure extra + * roll(s) of app icons is rendered at the bottom, so that they can fill the bottom gap + * created during predictive back's scale animation from all apps to home. + */ + @Override + protected void calculateExtraLayoutSpace(RecyclerView.State state, int[] extraLayoutSpace) { + super.calculateExtraLayoutSpace(state, extraLayoutSpace); + @Px int extraSpacePx = (int) (getHeight() + * (1 - AllAppsTransitionController.SWIPE_ALL_APPS_TO_HOME_MIN_SCALE) / 2); + extraLayoutSpace[1] = Math.max(extraLayoutSpace[1], extraSpacePx); + } + /** * Returns the number of rows before {@param adapterPosition}, including this position * which should not be counted towards the collection info. diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 24bfedb037..6f6f86b105 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -32,14 +32,18 @@ import android.animation.ObjectAnimator; import android.util.FloatProperty; import android.view.HapticFeedbackConstants; import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; import android.view.animation.Interpolator; import androidx.annotation.FloatRange; +import androidx.annotation.Nullable; import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; +import com.android.launcher3.R; import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.anim.AnimatorListeners; import com.android.launcher3.anim.Interpolators; @@ -66,7 +70,7 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener { // This constant should match the second derivative of the animator interpolator. public static final float INTERP_COEFF = 1.7f; - private static final float SWIPE_ALL_APPS_TO_HOME_MIN_SCALE = 0.9f; + public static final float SWIPE_ALL_APPS_TO_HOME_MIN_SCALE = 0.9f; private static final int REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS = 200; public static final FloatProperty ALL_APPS_PROGRESS = @@ -168,6 +172,8 @@ public class AllAppsTransitionController private boolean mIsTablet; + private boolean mHasScaleEffect; + public AllAppsTransitionController(Launcher l) { mLauncher = l; DeviceProfile dp = mLauncher.getDeviceProfile(); @@ -276,8 +282,18 @@ public class AllAppsTransitionController rv.getScrollbar().setVisibility(scaleProgress < 1f ? View.INVISIBLE : View.VISIBLE); } - // TODO(b/264906511): We need to disable view clipping on all apps' parent views so - // that the extra roll of app icons are displayed. + // Disable view clipping from all apps' RecyclerView up to all apps view during scale + // animation, and vice versa. The goal is to display extra roll(s) app icons (rendered in + // {@link AppsGridLayoutManager#calculateExtraLayoutSpace}) during scale animation. + boolean hasScaleEffect = scaleProgress < 1f; + if (hasScaleEffect != mHasScaleEffect) { + mHasScaleEffect = hasScaleEffect; + if (mHasScaleEffect) { + setClipChildrenOnViewTree(rv, mLauncher.getAppsView(), false); + } else { + restoreClipChildrenOnViewTree(rv, mLauncher.getAppsView()); + } + } } private void animateAllAppsToNoScale() { @@ -380,6 +396,79 @@ public class AllAppsTransitionController mShouldControlKeyboard = !mLauncher.getSearchConfig().isKeyboardSyncEnabled(); } + /** + * Recursively call {@link ViewGroup#setClipChildren(boolean)} from {@link View} to ts parent + * (direct or indirect) inclusive. This method will also save the old clipChildren value on each + * view with {@link View#setTag(int, Object)}, which can be restored in + * {@link #restoreClipChildrenOnViewTree(View, ViewParent)}. + * + * Note that if parent is null or not a parent of the view, this method will be applied all the + * way to root view. + * + * @param v child view + * @param parent direct or indirect parent of child view + * @param clipChildren whether we should clip children + */ + private static void setClipChildrenOnViewTree( + @Nullable View v, + @Nullable ViewParent parent, + boolean clipChildren) { + if (v == null) { + return; + } + + if (v instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) v; + boolean oldClipChildren = viewGroup.getClipChildren(); + if (oldClipChildren != clipChildren) { + v.setTag(R.id.saved_clip_children_tag_id, oldClipChildren); + viewGroup.setClipChildren(clipChildren); + } + } + + if (v == parent) { + return; + } + + if (v.getParent() instanceof View) { + setClipChildrenOnViewTree((View) v.getParent(), parent, clipChildren); + } + } + + /** + * Recursively call {@link ViewGroup#setClipChildren(boolean)} to restore clip children value + * set in {@link #setClipChildrenOnViewTree(View, ViewParent, boolean)} on view to its parent + * (direct or indirect) inclusive. + * + * Note that if parent is null or not a parent of the view, this method will be applied all the + * way to root view. + * + * @param v child view + * @param parent direct or indirect parent of child view + */ + private static void restoreClipChildrenOnViewTree( + @Nullable View v, @Nullable ViewParent parent) { + if (v == null) { + return; + } + if (v instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) v; + Object viewTag = viewGroup.getTag(R.id.saved_clip_children_tag_id); + if (viewTag instanceof Boolean) { + viewGroup.setClipChildren((boolean) viewTag); + viewGroup.setTag(R.id.saved_clip_children_tag_id, null); + } + } + + if (v == parent) { + return; + } + + if (v.getParent() instanceof View) { + restoreClipChildrenOnViewTree((View) v.getParent(), parent); + } + } + /** * Updates the total scroll range but does not update the UI. */ From d1b33b311c0028bc21e8c00c0866791860cddf40 Mon Sep 17 00:00:00 2001 From: Stefan Andonian Date: Fri, 16 Dec 2022 21:22:27 +0000 Subject: [PATCH 043/469] Expanded LauncherPrefs APIs to Replace Direct Shared Preference Usage. LauncherPrefs will contain Launcher's shared preference functionality. It controls optimizations and classifications such as restorable vs non-restorable data, bootaware vs non-bootaware data, and configurations such as default values so the calling code doesn't need to and our code base can have a single source of truth for items that are used in multiple places. The old APIs remain in place, but are deprecated and will be removed after all Shared Preference usage has been gated by LauncherPrefs in future CLs. Bug: 261635315 Test: Manually tested themed icon, Workspace configuration, and app install functionality. Change-Id: I29fd516468bc93fda393062e95be26b6d55c816e --- .../logging/SettingsChangeLogger.java | 4 +- .../android/launcher3/LauncherAppState.java | 14 +- src/com/android/launcher3/LauncherPrefs.kt | 261 +++++++++++++++++- .../allapps/BaseAllAppsContainerView.java | 7 +- .../launcher3/allapps/WorkEduCard.java | 4 +- .../launcher3/allapps/WorkProfileManager.java | 9 +- .../graphics/GridCustomizationsProvider.java | 9 +- .../launcher3/model/DeviceGridState.java | 27 +- .../launcher3/pm/InstallSessionHelper.java | 12 +- .../launcher3/provider/RestoreDbTask.java | 41 ++- src/com/android/launcher3/util/Themes.java | 4 +- .../android/launcher3/LauncherPrefsTest.kt | 148 ++++++++++ .../model/GridSizeMigrationUtilTest.kt | 9 +- .../launcher3/util/LauncherModelHelper.java | 3 +- 14 files changed, 462 insertions(+), 90 deletions(-) create mode 100644 tests/src/com/android/launcher3/LauncherPrefsTest.kt diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java index 5efc45e385..3d5c143413 100644 --- a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java +++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java @@ -16,6 +16,7 @@ package com.android.quickstep.logging; +import static com.android.launcher3.LauncherPrefs.THEMED_ICONS; import static com.android.launcher3.LauncherPrefs.getDevicePrefs; import static com.android.launcher3.LauncherPrefs.getPrefs; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_SCREEN_SUGGESTIONS_DISABLED; @@ -39,6 +40,7 @@ import android.util.Log; import android.util.Xml; import com.android.launcher3.AutoInstallsLayout; +import com.android.launcher3.LauncherPrefs; import com.android.launcher3.R; import com.android.launcher3.logging.InstanceId; import com.android.launcher3.logging.StatsLogManager; @@ -178,7 +180,7 @@ public class SettingsChangeLogger implements logger::log); SharedPreferences prefs = getPrefs(mContext); - logger.log(prefs.getBoolean(KEY_THEMED_ICONS, false) + logger.log(LauncherPrefs.get(mContext).get(THEMED_ICONS) ? LAUNCHER_THEMED_ICON_ENABLED : LAUNCHER_THEMED_ICON_DISABLED); diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 49659364b9..3461601f1d 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -18,7 +18,8 @@ package com.android.launcher3; import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED; -import static com.android.launcher3.LauncherPrefs.getDevicePrefs; +import static com.android.launcher3.LauncherPrefs.ICON_STATE; +import static com.android.launcher3.LauncherPrefs.THEMED_ICONS; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI; @@ -55,7 +56,7 @@ import com.android.launcher3.widget.custom.CustomWidgetManager; public class LauncherAppState implements SafeCloseable { public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher"; - private static final String KEY_ICON_STATE = "pref_icon_shape_path"; + public static final String KEY_ICON_STATE = "pref_icon_shape_path"; // We do not need any synchronization for this variable as its only written on UI thread. public static final MainThreadInitializedObject INSTANCE = @@ -117,10 +118,9 @@ public class LauncherAppState implements SafeCloseable { observer, MODEL_EXECUTOR.getHandler()); mOnTerminateCallback.add(iconChangeTracker::close); MODEL_EXECUTOR.execute(observer::verifyIconChanged); - SharedPreferences prefs = LauncherPrefs.getPrefs(mContext); - prefs.registerOnSharedPreferenceChangeListener(observer); + LauncherPrefs.get(context).addListener(observer, THEMED_ICONS); mOnTerminateCallback.add( - () -> prefs.unregisterOnSharedPreferenceChangeListener(observer)); + () -> LauncherPrefs.get(mContext).removeListener(observer, THEMED_ICONS)); InstallSessionTracker installSessionTracker = InstallSessionHelper.INSTANCE.get(context).registerInstallTracker(mModel); @@ -207,12 +207,12 @@ public class LauncherAppState implements SafeCloseable { public void onSystemIconStateChanged(String iconState) { IconShape.init(mContext); refreshAndReloadLauncher(); - getDevicePrefs(mContext).edit().putString(KEY_ICON_STATE, iconState).apply(); + LauncherPrefs.get(mContext).put(ICON_STATE, iconState); } void verifyIconChanged() { String iconState = mIconProvider.getSystemIconState(); - if (!iconState.equals(getDevicePrefs(mContext).getString(KEY_ICON_STATE, ""))) { + if (!iconState.equals(LauncherPrefs.get(mContext).get(ICON_STATE))) { onSystemIconStateChanged(iconState); } } diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt index 0d6ed04f2a..1fb2dce254 100644 --- a/src/com/android/launcher3/LauncherPrefs.kt +++ b/src/com/android/launcher3/LauncherPrefs.kt @@ -2,24 +2,255 @@ package com.android.launcher3 import android.content.Context import android.content.SharedPreferences +import android.content.SharedPreferences.OnSharedPreferenceChangeListener +import androidx.annotation.VisibleForTesting +import com.android.launcher3.allapps.WorkProfileManager +import com.android.launcher3.model.DeviceGridState +import com.android.launcher3.pm.InstallSessionHelper +import com.android.launcher3.provider.RestoreDbTask +import com.android.launcher3.util.MainThreadInitializedObject +import com.android.launcher3.util.Themes -object LauncherPrefs { +/** + * Use same context for shared preferences, so that we use a single cached instance + * TODO(b/262721340): Replace all direct SharedPreference refs with LauncherPrefs / Item methods. + */ +class LauncherPrefs(private val context: Context) { - @JvmStatic - fun getPrefs(context: Context): SharedPreferences { - // Use application context for shared preferences, so that we use a single cached instance - return context.applicationContext.getSharedPreferences( - LauncherFiles.SHARED_PREFERENCES_KEY, - Context.MODE_PRIVATE - ) + /** + * Retrieves the value for an [Item] from [SharedPreferences]. It handles method typing via the + * default value type, and will throw an error if the type of the item provided is not a + * `String`, `Boolean`, `Float`, `Int`, `Long`, or `Set`. + */ + @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST") + fun get(item: Item): T { + val sp = context.getSharedPreferences(item.sharedPrefFile, Context.MODE_PRIVATE) + + return when (item.defaultValue::class.java) { + String::class.java -> sp.getString(item.sharedPrefKey, item.defaultValue as String) + Boolean::class.java, + java.lang.Boolean::class.java -> + sp.getBoolean(item.sharedPrefKey, item.defaultValue as Boolean) + Int::class.java, + java.lang.Integer::class.java -> sp.getInt(item.sharedPrefKey, item.defaultValue as Int) + Float::class.java, + java.lang.Float::class.java -> + sp.getFloat(item.sharedPrefKey, item.defaultValue as Float) + Long::class.java, + java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, item.defaultValue as Long) + Set::class.java -> sp.getStringSet(item.sharedPrefKey, item.defaultValue as Set) + else -> + throw IllegalArgumentException( + "item type: ${item.defaultValue::class.java}" + + " is not compatible with sharedPref methods" + ) + } + as T } - @JvmStatic - fun getDevicePrefs(context: Context): SharedPreferences { - // Use application context for shared preferences, so that we use a single cached instance - return context.applicationContext.getSharedPreferences( - LauncherFiles.DEVICE_PREFERENCES_KEY, - Context.MODE_PRIVATE - ) + /** + * Stores each of the values provided in `SharedPreferences` according to the configuration + * contained within the associated items provided. Internally, it uses apply, so the caller + * cannot assume that the values that have been put are immediately available for use. + * + * The forEach loop is necessary here since there is 1 `SharedPreference.Editor` returned from + * prepareToPutValue(itemsToValues) for every distinct `SharedPreferences` file present in the + * provided item configurations. + */ + fun put(vararg itemsToValues: Pair, Any>): Unit = + prepareToPutValues(itemsToValues).forEach { it.apply() } + + /** + * Stores the value provided in `SharedPreferences` according to the item configuration provided + * It is asynchronous, so the caller can't assume that the value put is immediately available. + */ + fun put(item: Item, value: T): Unit = + context + .getSharedPreferences(item.sharedPrefFile, Context.MODE_PRIVATE) + .edit() + .putValue(item, value) + .apply() + + /** + * Synchronously stores all the values provided according to their associated Item + * configuration. + */ + fun putSync(vararg itemsToValues: Pair, Any>): Unit = + prepareToPutValues(itemsToValues).forEach { it.commit() } + + /** + * Update each shared preference file with the item - value pairs provided. This method is + * optimized to avoid retrieving the same shared preference file multiple times. + * + * @return `List` 1 for each distinct shared preference file among the + * items given as part of the itemsToValues parameter + */ + private fun prepareToPutValues( + itemsToValues: Array, Any>> + ): List = + itemsToValues + .groupBy { it.first.sharedPrefFile } + .map { fileToItemValueList -> + context + .getSharedPreferences(fileToItemValueList.key, Context.MODE_PRIVATE) + .edit() + .apply { + fileToItemValueList.value.forEach { itemToValue -> + putValue(itemToValue.first, itemToValue.second) + } + } + } + + /** + * Handles adding values to `SharedPreferences` regardless of type. This method is especially + * helpful for updating `SharedPreferences` values for `List<Any>` that have multiple + * types of Item values. + */ + @Suppress("UNCHECKED_CAST") + private fun SharedPreferences.Editor.putValue( + item: Item<*>, + value: Any + ): SharedPreferences.Editor = + when (value::class.java) { + String::class.java -> putString(item.sharedPrefKey, value as String) + Boolean::class.java, + java.lang.Boolean::class.java -> putBoolean(item.sharedPrefKey, value as Boolean) + Int::class.java, + java.lang.Integer::class.java -> putInt(item.sharedPrefKey, value as Int) + Float::class.java, + java.lang.Float::class.java -> putFloat(item.sharedPrefKey, value as Float) + Long::class.java, + java.lang.Long::class.java -> putLong(item.sharedPrefKey, value as Long) + Set::class.java -> putStringSet(item.sharedPrefKey, value as Set) + else -> + throw IllegalArgumentException( + "item type: " + + "${item.defaultValue!!::class} is not compatible with sharedPref methods" + ) + } + + /** + * After calling this method, the listener will be notified of any future updates to the + * `SharedPreferences` files associated with the provided list of items. The listener will need + * to filter update notifications so they don't activate for non-relevant updates. + */ + fun addListener(listener: OnSharedPreferenceChangeListener, vararg items: Item<*>) { + items + .map { it.sharedPrefFile } + .distinct() + .forEach { + context + .getSharedPreferences(it, Context.MODE_PRIVATE) + .registerOnSharedPreferenceChangeListener(listener) + } + } + + /** + * Stops the listener from getting notified of any more updates to any of the + * `SharedPreferences` files associated with any of the provided list of [Item]. + */ + fun removeListener(listener: OnSharedPreferenceChangeListener, vararg items: Item<*>) { + // If a listener is not registered to a SharedPreference, unregistering it does nothing + items + .map { it.sharedPrefFile } + .distinct() + .forEach { + context + .getSharedPreferences(it, Context.MODE_PRIVATE) + .unregisterOnSharedPreferenceChangeListener(listener) + } + } + + /** + * Checks if all the provided [Item] have values stored in their corresponding + * `SharedPreferences` files. + */ + fun has(vararg items: Item<*>): Boolean { + items + .groupBy { it.sharedPrefFile } + .forEach { (file, itemsSublist) -> + val prefs: SharedPreferences = + context.getSharedPreferences(file, Context.MODE_PRIVATE) + if (!itemsSublist.none { !prefs.contains(it.sharedPrefKey) }) return false + } + return true + } + + /** + * Asynchronously removes the [Item]'s value from its corresponding `SharedPreferences` file. + */ + fun remove(vararg items: Item<*>) = prepareToRemove(items).forEach { it.apply() } + + /** Synchronously removes the [Item]'s value from its corresponding `SharedPreferences` file. */ + fun removeSync(vararg items: Item<*>) = prepareToRemove(items).forEach { it.commit() } + + /** + * Creates `SharedPreferences.Editor` transactions for removing all the provided [Item] values + * from their respective `SharedPreferences` files. These returned `Editors` can then be + * committed or applied for synchronous or async behavior. + */ + private fun prepareToRemove(items: Array>): List = + items + .groupBy { it.sharedPrefFile } + .map { (file, items) -> + context.getSharedPreferences(file, Context.MODE_PRIVATE).edit().also { editor -> + items.forEach { item -> editor.remove(item.sharedPrefKey) } + } + } + + companion object { + @JvmField var INSTANCE = MainThreadInitializedObject { LauncherPrefs(it) } + + @JvmStatic fun get(context: Context): LauncherPrefs = INSTANCE.get(context) + + @JvmField val ICON_STATE = nonRestorableItem(LauncherAppState.KEY_ICON_STATE, "") + @JvmField val THEMED_ICONS = backedUpItem(Themes.KEY_THEMED_ICONS, false) + @JvmField val PROMISE_ICON_IDS = backedUpItem(InstallSessionHelper.PROMISE_ICON_IDS, "") + @JvmField val WORK_EDU_STEP = backedUpItem(WorkProfileManager.KEY_WORK_EDU_STEP, 0) + @JvmField val WORKSPACE_SIZE = backedUpItem(DeviceGridState.KEY_WORKSPACE_SIZE, "") + @JvmField val HOTSEAT_COUNT = backedUpItem(DeviceGridState.KEY_HOTSEAT_COUNT, -1) + @JvmField + val DEVICE_TYPE = + backedUpItem(DeviceGridState.KEY_DEVICE_TYPE, InvariantDeviceProfile.TYPE_PHONE) + @JvmField val DB_FILE = backedUpItem(DeviceGridState.KEY_DB_FILE, "") + @JvmField + val RESTORE_DEVICE = + backedUpItem(RestoreDbTask.RESTORED_DEVICE_TYPE, InvariantDeviceProfile.TYPE_PHONE) + @JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "") + @JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "") + + @VisibleForTesting + @JvmStatic + fun backedUpItem(sharedPrefKey: String, defaultValue: T): Item = + Item(sharedPrefKey, LauncherFiles.SHARED_PREFERENCES_KEY, defaultValue) + + @VisibleForTesting + @JvmStatic + fun nonRestorableItem(sharedPrefKey: String, defaultValue: T): Item = + Item(sharedPrefKey, LauncherFiles.DEVICE_PREFERENCES_KEY, defaultValue) + + @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.") + @JvmStatic + fun getPrefs(context: Context): SharedPreferences { + // Use application context for shared preferences, so we use single cached instance + return context.applicationContext.getSharedPreferences( + LauncherFiles.SHARED_PREFERENCES_KEY, + Context.MODE_PRIVATE + ) + } + + @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.") + @JvmStatic + fun getDevicePrefs(context: Context): SharedPreferences { + // Use application context for shared preferences, so we use a single cached instance + return context.applicationContext.getSharedPreferences( + LauncherFiles.DEVICE_PREFERENCES_KEY, + Context.MODE_PRIVATE + ) + } } } + +data class Item(val sharedPrefKey: String, val sharedPrefFile: String, val defaultValue: T) { + fun to(value: T): Pair, T> = Pair(this, value) +} diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java index 487807751d..1c676915fd 100644 --- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java @@ -57,7 +57,6 @@ import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.Insettable; import com.android.launcher3.InsettableFrameLayout; -import com.android.launcher3.LauncherPrefs; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider; @@ -161,10 +160,8 @@ public abstract class BaseAllAppsContainerView allApps, SharedPreferences prefs, + UserManager userManager, BaseAllAppsContainerView allApps, StatsLogManager statsLogManager) { mUserManager = userManager; mAllApps = allApps; - mPreferences = prefs; mMatcher = mAllApps.mPersonalMatcher.negate(); mStatsLogManager = statsLogManager; } @@ -225,7 +224,7 @@ public class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActiveP } private boolean isEduSeen() { - return mPreferences.getInt(KEY_WORK_EDU_STEP, 0) != 0; + return LauncherPrefs.get(mAllApps.getContext()).get(WORK_EDU_STEP) != 0; } private void onWorkFabClicked(View view) { diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java index feadafaf50..9426c22509 100644 --- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java +++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java @@ -1,8 +1,7 @@ package com.android.launcher3.graphics; -import static com.android.launcher3.LauncherPrefs.getPrefs; +import static com.android.launcher3.LauncherPrefs.THEMED_ICONS; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; -import static com.android.launcher3.util.Themes.KEY_THEMED_ICONS; import static com.android.launcher3.util.Themes.isThemedIconEnabled; import android.annotation.TargetApi; @@ -25,6 +24,7 @@ import android.util.Log; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.InvariantDeviceProfile.GridOption; +import com.android.launcher3.LauncherPrefs; import com.android.launcher3.Utilities; import com.android.launcher3.util.Executors; @@ -142,9 +142,8 @@ public class GridCustomizationsProvider extends ContentProvider { } case ICON_THEMED: case SET_ICON_THEMED: { - getPrefs(getContext()).edit() - .putBoolean(KEY_THEMED_ICONS, values.getAsBoolean(BOOLEAN_VALUE)) - .apply(); + LauncherPrefs.get(getContext()) + .put(THEMED_ICONS, values.getAsBoolean(BOOLEAN_VALUE)); return 1; } default: diff --git a/src/com/android/launcher3/model/DeviceGridState.java b/src/com/android/launcher3/model/DeviceGridState.java index 85d54c0d1c..edc8c1bcd9 100644 --- a/src/com/android/launcher3/model/DeviceGridState.java +++ b/src/com/android/launcher3/model/DeviceGridState.java @@ -17,7 +17,10 @@ package com.android.launcher3.model; import static com.android.launcher3.InvariantDeviceProfile.DeviceType; -import static com.android.launcher3.InvariantDeviceProfile.TYPE_PHONE; +import static com.android.launcher3.LauncherPrefs.DB_FILE; +import static com.android.launcher3.LauncherPrefs.DEVICE_TYPE; +import static com.android.launcher3.LauncherPrefs.HOTSEAT_COUNT; +import static com.android.launcher3.LauncherPrefs.WORKSPACE_SIZE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_2; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_3; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_4; @@ -25,7 +28,6 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_6; import android.content.Context; -import android.content.SharedPreferences; import android.text.TextUtils; import com.android.launcher3.InvariantDeviceProfile; @@ -58,11 +60,11 @@ public class DeviceGridState implements Comparable { } public DeviceGridState(Context context) { - SharedPreferences prefs = LauncherPrefs.getPrefs(context); - mGridSizeString = prefs.getString(KEY_WORKSPACE_SIZE, ""); - mNumHotseat = prefs.getInt(KEY_HOTSEAT_COUNT, -1); - mDeviceType = prefs.getInt(KEY_DEVICE_TYPE, TYPE_PHONE); - mDbFile = prefs.getString(KEY_DB_FILE, ""); + LauncherPrefs lp = LauncherPrefs.get(context); + mGridSizeString = lp.get(WORKSPACE_SIZE); + mNumHotseat = lp.get(HOTSEAT_COUNT); + mDeviceType = lp.get(DEVICE_TYPE); + mDbFile = lp.get(DB_FILE); } /** @@ -90,12 +92,11 @@ public class DeviceGridState implements Comparable { * Stores the device state to shared preferences */ public void writeToPrefs(Context context) { - LauncherPrefs.getPrefs(context).edit() - .putString(KEY_WORKSPACE_SIZE, mGridSizeString) - .putInt(KEY_HOTSEAT_COUNT, mNumHotseat) - .putInt(KEY_DEVICE_TYPE, mDeviceType) - .putString(KEY_DB_FILE, mDbFile) - .apply(); + LauncherPrefs.get(context).put( + WORKSPACE_SIZE.to(mGridSizeString), + HOTSEAT_COUNT.to(mNumHotseat), + DEVICE_TYPE.to(mDeviceType), + DB_FILE.to(mDbFile)); } /** diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java index 150bca4058..db235668db 100644 --- a/src/com/android/launcher3/pm/InstallSessionHelper.java +++ b/src/com/android/launcher3/pm/InstallSessionHelper.java @@ -16,8 +16,6 @@ package com.android.launcher3.pm; -import static com.android.launcher3.LauncherPrefs.getPrefs; - import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.LauncherApps; @@ -34,6 +32,7 @@ import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.annotation.WorkerThread; +import com.android.launcher3.LauncherPrefs; import com.android.launcher3.LauncherSettings; import com.android.launcher3.SessionCommitReceiver; import com.android.launcher3.Utilities; @@ -65,7 +64,7 @@ public class InstallSessionHelper { // Set of session ids of promise icons that have been added to the home screen // as FLAG_PROMISE_NEW_INSTALLS. @NonNull - protected static final String PROMISE_ICON_IDS = "promise_icon_ids"; + public static final String PROMISE_ICON_IDS = "promise_icon_ids"; private static final boolean DEBUG = false; @@ -102,7 +101,7 @@ public class InstallSessionHelper { return mPromiseIconIds; } mPromiseIconIds = IntSet.wrap(IntArray.fromConcatString( - getPrefs(mAppContext).getString(PROMISE_ICON_IDS, ""))); + LauncherPrefs.get(mAppContext).get(LauncherPrefs.PROMISE_ICON_IDS))); IntArray existingIds = new IntArray(); for (SessionInfo info : getActiveSessions().values()) { @@ -146,9 +145,8 @@ public class InstallSessionHelper { } private void updatePromiseIconPrefs() { - getPrefs(mAppContext).edit() - .putString(PROMISE_ICON_IDS, getPromiseIconIds().getArray().toConcatString()) - .apply(); + LauncherPrefs.get(mAppContext).put(LauncherPrefs.PROMISE_ICON_IDS, + getPromiseIconIds().getArray().toConcatString()); } @Nullable diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java index 5e97b2dedd..2a452be8dd 100644 --- a/src/com/android/launcher3/provider/RestoreDbTask.java +++ b/src/com/android/launcher3/provider/RestoreDbTask.java @@ -17,13 +17,14 @@ package com.android.launcher3.provider; import static com.android.launcher3.InvariantDeviceProfile.TYPE_MULTI_DISPLAY; -import static com.android.launcher3.InvariantDeviceProfile.TYPE_PHONE; +import static com.android.launcher3.LauncherPrefs.APP_WIDGET_IDS; +import static com.android.launcher3.LauncherPrefs.OLD_APP_WIDGET_IDS; +import static com.android.launcher3.LauncherPrefs.RESTORE_DEVICE; import static com.android.launcher3.provider.LauncherDbUtils.dropTable; import android.app.backup.BackupManager; import android.content.ContentValues; import android.content.Context; -import android.content.SharedPreferences; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.UserHandle; @@ -62,13 +63,13 @@ import java.util.Arrays; public class RestoreDbTask { private static final String TAG = "RestoreDbTask"; - private static final String RESTORED_DEVICE_TYPE = "restored_task_pending"; + public static final String RESTORED_DEVICE_TYPE = "restored_task_pending"; private static final String INFO_COLUMN_NAME = "name"; private static final String INFO_COLUMN_DEFAULT_VALUE = "dflt_value"; - private static final String APPWIDGET_OLD_IDS = "appwidget_old_ids"; - private static final String APPWIDGET_IDS = "appwidget_ids"; + public static final String APPWIDGET_OLD_IDS = "appwidget_old_ids"; + public static final String APPWIDGET_IDS = "appwidget_ids"; /** * Tries to restore the backup DB if needed @@ -87,7 +88,7 @@ public class RestoreDbTask { // Set is pending to false irrespective of the result, so that it doesn't get // executed again. - LauncherPrefs.getPrefs(context).edit().remove(RESTORED_DEVICE_TYPE).commit(); + LauncherPrefs.get(context).removeSync(RESTORE_DEVICE); idp.reinitializeAfterRestore(context); } @@ -240,8 +241,7 @@ public class RestoreDbTask { } // If restored from a single display backup, remove gaps between screenIds - if (LauncherPrefs.getPrefs(context).getInt(RESTORED_DEVICE_TYPE, TYPE_PHONE) - != TYPE_MULTI_DISPLAY) { + if (LauncherPrefs.get(context).get(RESTORE_DEVICE) != TYPE_MULTI_DISPLAY) { removeScreenIdGaps(db); } @@ -339,7 +339,7 @@ public class RestoreDbTask { } public static boolean isPending(Context context) { - return LauncherPrefs.getPrefs(context).contains(RESTORED_DEVICE_TYPE); + return LauncherPrefs.get(context).has(RESTORE_DEVICE); } /** @@ -347,34 +347,31 @@ public class RestoreDbTask { */ public static void setPending(Context context) { FileLog.d(TAG, "Restore data received through full backup "); - LauncherPrefs.getPrefs(context).edit() - .putInt(RESTORED_DEVICE_TYPE, new DeviceGridState(context).getDeviceType()) - .commit(); + LauncherPrefs.get(context) + .putSync(RESTORE_DEVICE.to(new DeviceGridState(context).getDeviceType())); } private void restoreAppWidgetIdsIfExists(Context context) { - SharedPreferences prefs = LauncherPrefs.getPrefs(context); - if (prefs.contains(APPWIDGET_OLD_IDS) && prefs.contains(APPWIDGET_IDS)) { + LauncherPrefs lp = LauncherPrefs.get(context); + if (lp.has(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS)) { LauncherWidgetHolder holder = LauncherWidgetHolder.newInstance(context); AppWidgetsRestoredReceiver.restoreAppWidgetIds(context, - IntArray.fromConcatString(prefs.getString(APPWIDGET_OLD_IDS, "")).toArray(), - IntArray.fromConcatString(prefs.getString(APPWIDGET_IDS, "")).toArray(), + IntArray.fromConcatString(lp.get(OLD_APP_WIDGET_IDS)).toArray(), + IntArray.fromConcatString(lp.get(APP_WIDGET_IDS)).toArray(), holder); holder.destroy(); } else { FileLog.d(TAG, "No app widget ids to restore."); } - prefs.edit().remove(APPWIDGET_OLD_IDS) - .remove(APPWIDGET_IDS).apply(); + lp.remove(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS); } public static void setRestoredAppWidgetIds(Context context, @NonNull int[] oldIds, @NonNull int[] newIds) { - LauncherPrefs.getPrefs(context).edit() - .putString(APPWIDGET_OLD_IDS, IntArray.wrap(oldIds).toConcatString()) - .putString(APPWIDGET_IDS, IntArray.wrap(newIds).toConcatString()) - .commit(); + LauncherPrefs.get(context).putSync( + OLD_APP_WIDGET_IDS.to(IntArray.wrap(oldIds).toConcatString()), + APP_WIDGET_IDS.to(IntArray.wrap(newIds).toConcatString())); } } diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java index 585bea93ad..5526839ce7 100644 --- a/src/com/android/launcher3/util/Themes.java +++ b/src/com/android/launcher3/util/Themes.java @@ -19,6 +19,8 @@ package com.android.launcher3.util; import static android.app.WallpaperColors.HINT_SUPPORTS_DARK_TEXT; import static android.app.WallpaperColors.HINT_SUPPORTS_DARK_THEME; +import static com.android.launcher3.LauncherPrefs.THEMED_ICONS; + import android.app.WallpaperColors; import android.app.WallpaperManager; import android.content.Context; @@ -74,7 +76,7 @@ public class Themes { * Returns true if workspace icon theming is enabled */ public static boolean isThemedIconEnabled(Context context) { - return LauncherPrefs.getPrefs(context).getBoolean(KEY_THEMED_ICONS, false); + return LauncherPrefs.get(context).get(THEMED_ICONS); } public static String getDefaultBodyFont(Context context) { diff --git a/tests/src/com/android/launcher3/LauncherPrefsTest.kt b/tests/src/com/android/launcher3/LauncherPrefsTest.kt new file mode 100644 index 0000000000..151abf1040 --- /dev/null +++ b/tests/src/com/android/launcher3/LauncherPrefsTest.kt @@ -0,0 +1,148 @@ +package com.android.launcher3 + +import android.content.SharedPreferences.OnSharedPreferenceChangeListener +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry +import com.google.common.truth.Truth.assertThat +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import org.junit.Test +import org.junit.runner.RunWith + +private val TEST_BOOLEAN_ITEM = LauncherPrefs.nonRestorableItem("1", false) +private val TEST_STRING_ITEM = LauncherPrefs.nonRestorableItem("2", "( ͡❛ ͜ʖ ͡❛)") +private val TEST_INT_ITEM = LauncherPrefs.nonRestorableItem("3", -1) + +@SmallTest +@RunWith(AndroidJUnit4::class) +class LauncherPrefsTest { + + private val launcherPrefs by lazy { + LauncherPrefs.get(InstrumentationRegistry.getInstrumentation().targetContext).apply { + remove(TEST_BOOLEAN_ITEM, TEST_STRING_ITEM, TEST_INT_ITEM) + } + } + + @Test + fun has_keyMissingFromLauncherPrefs_returnsFalse() { + assertThat(launcherPrefs.has(TEST_BOOLEAN_ITEM)).isFalse() + } + + @Test + fun has_keyPresentInLauncherPrefs_returnsTrue() { + with(launcherPrefs) { + putSync(TEST_BOOLEAN_ITEM.to(TEST_BOOLEAN_ITEM.defaultValue)) + assertThat(has(TEST_BOOLEAN_ITEM)).isTrue() + remove(TEST_BOOLEAN_ITEM) + } + } + + @Test + fun addListener_listeningForStringItemUpdates_isCorrectlyNotifiedOfUpdates() { + val latch = CountDownLatch(1) + val listener = OnSharedPreferenceChangeListener { _, _ -> latch.countDown() } + + with(launcherPrefs) { + putSync(TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue)) + addListener(listener, TEST_STRING_ITEM) + putSync(TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue + "abc")) + + assertThat(latch.await(2, TimeUnit.SECONDS)).isTrue() + remove(TEST_STRING_ITEM) + } + } + + @Test + fun removeListener_previouslyListeningForStringItemUpdates_isNoLongerNotifiedOfUpdates() { + val latch = CountDownLatch(1) + val listener = OnSharedPreferenceChangeListener { _, _ -> latch.countDown() } + + with(launcherPrefs) { + addListener(listener, TEST_STRING_ITEM) + removeListener(listener, TEST_STRING_ITEM) + putSync(TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue + "hello.")) + + // latch will be still be 1 (and await will return false) if the listener was not called + assertThat(latch.await(2, TimeUnit.SECONDS)).isFalse() + remove(TEST_STRING_ITEM) + } + } + + @Test + fun addListenerAndRemoveListener_forMultipleItems_bothWorkProperly() { + var latch = CountDownLatch(3) + val listener = OnSharedPreferenceChangeListener { _, _ -> latch.countDown() } + + with(launcherPrefs) { + addListener(listener, TEST_INT_ITEM, TEST_STRING_ITEM, TEST_BOOLEAN_ITEM) + putSync( + TEST_INT_ITEM.to(TEST_INT_ITEM.defaultValue + 123), + TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue + "abc"), + TEST_BOOLEAN_ITEM.to(!TEST_BOOLEAN_ITEM.defaultValue) + ) + assertThat(latch.await(2, TimeUnit.SECONDS)).isTrue() + + removeListener(listener, TEST_INT_ITEM, TEST_STRING_ITEM, TEST_BOOLEAN_ITEM) + latch = CountDownLatch(1) + putSync( + TEST_INT_ITEM.to(TEST_INT_ITEM.defaultValue), + TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue), + TEST_BOOLEAN_ITEM.to(TEST_BOOLEAN_ITEM.defaultValue) + ) + remove(TEST_INT_ITEM, TEST_STRING_ITEM, TEST_BOOLEAN_ITEM) + + assertThat(latch.await(2, TimeUnit.SECONDS)).isFalse() + } + } + + @Test + fun get_booleanItemNotInLauncherprefs_returnsDefaultValue() { + assertThat(launcherPrefs.get(TEST_BOOLEAN_ITEM)).isEqualTo(TEST_BOOLEAN_ITEM.defaultValue) + } + + @Test + fun get_stringItemNotInLauncherPrefs_returnsDefaultValue() { + assertThat(launcherPrefs.get(TEST_STRING_ITEM)).isEqualTo(TEST_STRING_ITEM.defaultValue) + } + + @Test + fun get_intItemNotInLauncherprefs_returnsDefaultValue() { + assertThat(launcherPrefs.get(TEST_INT_ITEM)).isEqualTo(TEST_INT_ITEM.defaultValue) + } + + @Test + fun put_storesItemInLauncherPrefs_successfully() { + val notDefaultValue = !TEST_BOOLEAN_ITEM.defaultValue + + with(launcherPrefs) { + putSync(TEST_BOOLEAN_ITEM.to(notDefaultValue)) + assertThat(get(TEST_BOOLEAN_ITEM)).isEqualTo(notDefaultValue) + remove(TEST_BOOLEAN_ITEM) + } + } + + @Test + fun put_storesListOfItemsInLauncherPrefs_successfully() { + with(launcherPrefs) { + putSync( + TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue), + TEST_INT_ITEM.to(TEST_INT_ITEM.defaultValue), + TEST_BOOLEAN_ITEM.to(TEST_BOOLEAN_ITEM.defaultValue) + ) + assertThat(has(TEST_BOOLEAN_ITEM, TEST_INT_ITEM, TEST_STRING_ITEM)).isTrue() + remove(TEST_STRING_ITEM, TEST_INT_ITEM, TEST_BOOLEAN_ITEM) + } + } + + @Test + fun remove_deletesItemFromLauncherPrefs_successfully() { + val notDefaultValue = !TEST_BOOLEAN_ITEM.defaultValue + + with(launcherPrefs) { + putSync(TEST_BOOLEAN_ITEM.to(notDefaultValue)) + remove(TEST_BOOLEAN_ITEM) + assertThat(get(TEST_BOOLEAN_ITEM)).isEqualTo(TEST_BOOLEAN_ITEM.defaultValue) + } + } +} diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt index dcc8ec732a..a85fa3a4d1 100644 --- a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt +++ b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt @@ -24,7 +24,8 @@ import android.os.Process import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.launcher3.InvariantDeviceProfile -import com.android.launcher3.LauncherFiles +import com.android.launcher3.LauncherPrefs +import com.android.launcher3.LauncherPrefs.Companion.WORKSPACE_SIZE import com.android.launcher3.LauncherSettings.Favorites.* import com.android.launcher3.config.FeatureFlags import com.android.launcher3.model.GridSizeMigrationUtil.DbReader @@ -754,11 +755,7 @@ class GridSizeMigrationUtilTest { .edit() .putBoolean(FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC.key, true) .commit() - context - .getSharedPreferences(LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE) - .edit() - .putString(DeviceGridState.KEY_WORKSPACE_SIZE, srcGridSize) - .commit() + LauncherPrefs.get(context).putSync(WORKSPACE_SIZE.to(srcGridSize)) FeatureFlags.initialize(context) } diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java index 93bf31256d..caec301a9f 100644 --- a/tests/src/com/android/launcher3/util/LauncherModelHelper.java +++ b/tests/src/com/android/launcher3/util/LauncherModelHelper.java @@ -55,6 +55,7 @@ import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherModel.ModelUpdateTask; +import com.android.launcher3.LauncherPrefs; import com.android.launcher3.LauncherProvider; import com.android.launcher3.LauncherSettings; import com.android.launcher3.model.AllAppsList; @@ -506,7 +507,7 @@ public class LauncherModelHelper { SanboxModelContext() { super(ApplicationProvider.getApplicationContext(), - UserCache.INSTANCE, InstallSessionHelper.INSTANCE, + UserCache.INSTANCE, InstallSessionHelper.INSTANCE, LauncherPrefs.INSTANCE, LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE, DisplayController.INSTANCE, CustomWidgetManager.INSTANCE, SettingsCache.INSTANCE, PluginManagerWrapper.INSTANCE, From a34521e4345ee34cdadf4ecbd980aa896b55a65c Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 12 Jan 2023 16:09:24 -0800 Subject: [PATCH 044/469] Updating TaskbarStashController API > Making the state animator nonNull to avoid null checks at caller > Updating the methods names to explicitely indication animation being started or not Bug: 265352919 Test: Verified on device Change-Id: I1f5b47921f831ed0b202aced10825c8e1d39a4dc --- .../taskbar/FallbackTaskbarUIController.java | 8 +-- .../TaskbarLauncherStateController.java | 4 +- .../taskbar/TaskbarStashController.java | 69 ++++++------------- 3 files changed, 28 insertions(+), 53 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java index df867cb831..474dc3d338 100644 --- a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java @@ -73,12 +73,12 @@ public class FallbackTaskbarUIController extends TaskbarUIController { boolean useStashedLauncherState = toState.hasOverviewActions(); boolean stashedLauncherState = useStashedLauncherState && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get(); - TaskbarStashController controller = mControllers.taskbarStashController; + TaskbarStashController stashController = mControllers.taskbarStashController; // Set both FLAG_IN_STASHED_LAUNCHER_STATE and FLAG_IN_APP to ensure the state is respected. // For all other states, just use the current stashed-in-app setting (e.g. if long clicked). - controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, stashedLauncherState); - controller.updateStateForFlag(FLAG_IN_APP, !useStashedLauncherState); - return controller.applyStateWithoutStart(duration); + stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, stashedLauncherState); + stashController.updateStateForFlag(FLAG_IN_APP, !useStashedLauncherState); + return stashController.createApplyStateAnimator(duration); } private void animateToRecentsState(RecentsState toState) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index 7a75661329..b7cc76e9f1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -184,7 +184,7 @@ import java.util.StringJoiner; stashController.updateStateForFlag(FLAG_IN_APP, false); updateStateForFlag(FLAG_RECENTS_ANIMATION_RUNNING, true); - animatorSet.play(stashController.applyStateWithoutStart(duration)); + animatorSet.play(stashController.createApplyStateAnimator(duration)); animatorSet.play(applyState(duration, false)); if (mTaskBarRecentsAnimationListener != null) { @@ -398,7 +398,7 @@ import java.util.StringJoiner; boolean isInStashedState = mLauncherState.isTaskbarStashed(mLauncher); TaskbarStashController stashController = mControllers.taskbarStashController; stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, isInStashedState); - Animator stashAnimator = stashController.applyStateWithoutStart(duration); + Animator stashAnimator = stashController.createApplyStateAnimator(duration); if (stashAnimator != null) { stashAnimator.addListener(new AnimatorListenerAdapter() { @Override diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index 6031b49874..61d169ff65 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -35,6 +35,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_S import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; +import android.animation.ValueAnimator; import android.annotation.Nullable; import android.content.SharedPreferences; import android.content.res.Resources; @@ -69,8 +70,6 @@ import java.util.function.IntPredicate; */ public class TaskbarStashController implements TaskbarControllers.LoggableTaskbarController { - private static final String TAG = "TaskbarStashController"; - public static final int FLAG_IN_APP = 1 << 0; public static final int FLAG_STASHED_IN_APP_MANUAL = 1 << 1; // long press, persisted public static final int FLAG_STASHED_IN_SYSUI_STATE = 1 << 2; // app pinning, keyguard, etc. @@ -374,8 +373,8 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba /** * Returns the height that taskbar will inset when inside apps. - * @see WindowInsets.Type#navigationBars() - * @see WindowInsets.Type#systemBars() + * @see android.view.WindowInsets.Type#navigationBars() + * @see android.view.WindowInsets.Type#systemBars() */ public int getContentHeightToReportToApps() { if ((isPhoneMode() && !mActivity.isThreeButtonNav()) @@ -408,7 +407,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba /** * Returns the height that taskbar will inset when inside apps. - * @see WindowInsets.Type#tappableElement() + * @see android.view.WindowInsets.Type#tappableElement() */ public int getTappableHeightToReportToApps() { int contentHeight = getContentHeightToReportToApps(); @@ -494,7 +493,6 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba createAnimToIsStashed( /* isStashed= */ false, placeholderDuration, - /* startDelay= */ 0, /* animateBg= */ false, /* changedFlags=*/ 0); animation.play(mAnimator); @@ -504,11 +502,10 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba * Create a stash animation and save to {@link #mAnimator}. * @param isStashed whether it's a stash animation or an unstash animation * @param duration duration of the animation - * @param startDelay how many milliseconds to delay the animation after starting it. * @param animateBg whether the taskbar's background should be animated */ - private void createAnimToIsStashed(boolean isStashed, long duration, long startDelay, - boolean animateBg, int changedFlags) { + private void createAnimToIsStashed(boolean isStashed, long duration, boolean animateBg, + int changedFlags) { if (mAnimator != null) { mAnimator.cancel(); } @@ -528,13 +525,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba .setDuration(duration)); mAnimator.play(mTaskbarImeBgAlpha.animateToValue( hasAnyFlag(FLAG_STASHED_IN_APP_IME) ? 0 : 1).setDuration(duration)); - mAnimator.setStartDelay(startDelay); - mAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mAnimator = null; - } - }); + mAnimator.addListener(AnimatorListeners.forEndCallback(() -> mAnimator = null)); return; } @@ -615,7 +606,6 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba mAnimator.playTogether(fullLengthAnimatorSet, firstHalfAnimatorSet, secondHalfAnimatorSet); - mAnimator.setStartDelay(startDelay); mAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { @@ -701,19 +691,17 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba } public void applyState(long duration) { - mStatePropertyHolder.setState(mState, duration, true); + createApplyStateAnimator(duration).start(); } public void applyState(long duration, long startDelay) { - mStatePropertyHolder.setState(mState, duration, startDelay, true); + Animator animator = createApplyStateAnimator(duration); + animator.setStartDelay(startDelay); + animator.start(); } - public Animator applyStateWithoutStart() { - return applyStateWithoutStart(TASKBAR_STASH_DURATION); - } - - public Animator applyStateWithoutStart(long duration) { - return mStatePropertyHolder.setState(mState, duration, false); + public Animator createApplyStateAnimator(long duration) { + return mStatePropertyHolder.createSetStateAnimator(mState, duration); } /** @@ -948,22 +936,14 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba } /** - * @see #setState(int, long, long, boolean) with a default startDelay = 0. - */ - public Animator setState(int flags, long duration, boolean start) { - return setState(flags, duration, 0 /* startDelay */, start); - } - - /** - * Applies the latest state, potentially calling onStateChangeApplied() and creating a new - * animation (stored in mAnimator) which is started if {@param start} is true. + * Creates an animator (stored in mAnimator) which applies the latest state, potentially + * creating a new animation (stored in mAnimator). * @param flags The latest flags to apply (see the top of this file). * @param duration The length of the animation. - * @param startDelay How long to delay the animation after calling start(). - * @param start Whether to start mAnimator immediately. - * @return mAnimator if mIsStashed changed, else null. + * @return mAnimator if mIsStashed changed or an empty animator. */ - public Animator setState(int flags, long duration, long startDelay, boolean start) { + @NonNull + public Animator createSetStateAnimator(int flags, long duration) { int changedFlags = mPrevFlags ^ flags; if (mPrevFlags != flags) { onStateChangeApplied(changedFlags); @@ -979,24 +959,19 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba && mAnimator != null && mAnimator.isStarted())) { if (TestProtocol.sDebugTracing) { Log.d(TestProtocol.TASKBAR_IN_APP_STATE, String.format( - "setState: mIsStashed=%b, isStashed=%b, duration=%d, start=:%b", + "setState: mIsStashed=%b, isStashed=%b, duration=%d", mIsStashed, isStashed, - duration, - start)); + duration)); } mIsStashed = isStashed; mIsHotseatIconOnTopWhenAligned = isHotseatIconOnTopWhenAligned; // This sets mAnimator. - createAnimToIsStashed( - mIsStashed, duration, startDelay, /* animateBg= */ true, changedFlags); - if (start) { - mAnimator.start(); - } + createAnimToIsStashed(mIsStashed, duration, /* animateBg= */ true, changedFlags); return mAnimator; } - return null; + return ValueAnimator.ofFloat(0, 1).setDuration(duration); } } } From c084cd761e36070e262f222fd2fefdb9b8fbc68a Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sun, 15 Jan 2023 20:52:29 -0800 Subject: [PATCH 045/469] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I755d591028eba2dfa55fea32f1461472ec7da3d0 --- quickstep/res/values-ky/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml index 55e5a11872..bb22c2f15c 100644 --- a/quickstep/res/values-ky/strings.xml +++ b/quickstep/res/values-ky/strings.xml @@ -37,7 +37,7 @@ "Көп иштетилген колдонмолорго Башкы экрандан оңой кириңиз. Сунуштар тартиптин негизинде өзгөрөт. Тандалмалардын катарындагы колдонмолор башкы экраныңызга жылдырылат." "Сунушталган колдонолорду алуу" "Жок, рахмат" - "Жөндөөлөр" + "Параметрлер" "Көп иштетилген колдонмолор ушул жерде көрүнүп, тартиптин негизинде өзгөрөт" "Сунуштарды алып туруу үчүн ылдый жактагы тилкедеги колдонмолорду сүйрөп келиңиз" "Сунушталган колдонмолор бош жерге кошулат" @@ -71,7 +71,7 @@ "Бир колдонмодон экинчисине өтүү үчүн экранды 2 манжа менен ылдыйдан өйдө сүрүп, коё бербей туруңуз." "Дапдаяр!" "Бүттү" - "Жөндөөлөр" + "Параметрлер" "Кайталап көрүңүз" "Сонун!" "Үйрөткүч: %1$d/%2$d" From ef6ec03345754122ff7d9aba7b80427a27c9a4e4 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sun, 15 Jan 2023 20:53:18 -0800 Subject: [PATCH 046/469] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I2ee7d4e8b783c974f15524b54a5a456fa2fedd03 --- go/quickstep/res/values-ky/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/quickstep/res/values-ky/strings.xml b/go/quickstep/res/values-ky/strings.xml index e4a2474669..55e70c8cdf 100644 --- a/go/quickstep/res/values-ky/strings.xml +++ b/go/quickstep/res/values-ky/strings.xml @@ -9,7 +9,7 @@ "ЖОККО ЧЫГАРУУ" "ЖӨНДӨӨЛӨР" "Экрандагы текстти которуу же угуу" - "Экрандагы текст, веб-даректер жана скриншоттор сыяктуу маалымат Google менен бөлүшүлүшү мүмкүн.\n\nБөлүшүлгөн маалыматты өзгөртүү үчүн""Жөндөөлөр > Колдонмолор > Демейки колдонмолор > Санариптик жардамчы колдонмосуна өтүңүз""." + "Экрандагы текст, веб-даректер жана скриншоттор сыяктуу маалымат Google менен бөлүшүлүшү мүмкүн.\n\nБөлүшүлгөн маалыматты өзгөртүү үчүн""Параметрлер > Колдонмолор > Демейки колдонмолор > Санариптик жардамчы колдонмосуна өтүңүз""." "Бул функцияны колдонуу үчүн жардамчыны тандаңыз" "Экраныңыздагы текстти угуу же которуу үчүн Жөндөөлөрдөн санариптик жардамчы колдонмосун тандаңыз" "Бул функцияны колдонуу үчүн жардамчыңызды өзгөртүңүз" From e21933ba5cc5267a3198a0f09df5afbd6634a482 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sun, 15 Jan 2023 20:54:28 -0800 Subject: [PATCH 047/469] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I88861b30cdbee63074ec871aa3abfece14666488 --- res/values-pt/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index 98e8607185..d97ba0f248 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -32,7 +32,7 @@ "Dividir para a esquerda" "Dividir para a direita" "Informações do app %1$s" - "Toque e mantenha pressionado para mover um widget." + "Toque e pressione para mover um widget." "Toque duas vezes e mantenha a tela pressionada para mover um widget ou usar ações personalizadas." "%1$d × %2$d" "%1$d de largura por %2$d de altura" From 3f5d510b381c91fe83df72e543a6dcd95201bd24 Mon Sep 17 00:00:00 2001 From: Tony Huang Date: Thu, 12 Jan 2023 13:33:27 +0800 Subject: [PATCH 048/469] Fix launcher crash by set divider hidden We will hide divider when isLikelyToStartNewTask become true, but this call sometime earlier than onRecentsAnimationStart then cause crash because mRecentsAnimationTargets is still null. Fix this by checking mRecentsAnimationTargets before set divider visibility. And also add new condition to hide divider to ensure it hidden if such case happened. Fix: 265238266 Test: manaul Test: pass existing tests Change-Id: I80b1294e69a52e7ac5255cd8e55e7c5e6a3dcbcb --- .../src/com/android/quickstep/AbsSwipeUpHandler.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 45f6742bb8..14b01fe7d1 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -324,6 +324,7 @@ public abstract class AbsSwipeUpHandler, // May be set to false when mIsTransientTaskbar is true. private boolean mCanSlowSwipeGoHome = true; private boolean mHasReachedOverviewThreshold = false; + private boolean mDividerHiddenBeforeAnimation = false; @Nullable private RemoteAnimationTargets.ReleaseCheck mSwipePipToHomeReleaseCheck = null; @@ -1677,7 +1678,8 @@ public abstract class AbsSwipeUpHandler, mRecentsAnimationController.enableInputConsumer(); // Start hiding the divider - if (!mIsTransientTaskbar || mTaskbarAlreadyOpen || mIsTaskbarAllAppsOpen) { + if (!mIsTransientTaskbar || mTaskbarAlreadyOpen || mIsTaskbarAllAppsOpen + || mDividerHiddenBeforeAnimation) { setDividerShown(false /* shown */, true /* immediate */); } } @@ -2327,6 +2329,12 @@ public abstract class AbsSwipeUpHandler, } private void setDividerShown(boolean shown, boolean immediate) { + if (mRecentsAnimationTargets == null) { + if (!shown) { + mDividerHiddenBeforeAnimation = true; + } + return; + } if (mDividerAnimator != null) { mDividerAnimator.cancel(); } From de1fe14a474878d304bec6b3e30357f5f9cd48c9 Mon Sep 17 00:00:00 2001 From: Sebastian Franco Date: Thu, 8 Dec 2022 11:08:00 -0800 Subject: [PATCH 049/469] Adding the avility to add icons on multiple CellLayouts on tests. I needed to change the code so that you can pass the CellX/Y position to the drag function instead of the pixel coordinates and we can pass X position bigger than one CellLayout. Also, I notice that getCellCenter only worked for 1x1 items so I added the option to specify the widget size. Fix: 262431878 Test: atest ReorderWidgets#multipleCellLayoutsSimpleReorder Change-Id: I3b09de770e8f99aa10cae8cea32e7e6bc02c7b8b --- .../testing/TestInformationHandler.java | 7 ++ .../testing/shared/TestProtocol.java | 1 + .../shared/WorkspaceCellCenterRequest.java | 2 +- .../launcher3/celllayout/CellLayoutBoard.java | 113 ++++++++++++++++-- .../MultipleCellLayoutsSimpleReorder.java | 88 ++++++++++++++ .../launcher3/celllayout/ReorderWidgets.java | 77 ++++++------ .../celllayout/TestWorkspaceBuilder.java | 26 ++-- .../celllayout/testcases/ReorderTestCase.java | 10 +- .../testcases/SimpleReorderCase.java | 28 ++++- .../launcher3/ui/AbstractLauncherUiTest.java | 2 +- .../com/android/launcher3/tapl/Widget.java | 23 ++-- .../com/android/launcher3/tapl/Workspace.java | 76 +++++++++++- 12 files changed, 364 insertions(+), 89 deletions(-) create mode 100644 tests/src/com/android/launcher3/celllayout/MultipleCellLayoutsSimpleReorder.java diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java index acb7eb38a2..9a34478918 100644 --- a/src/com/android/launcher3/testing/TestInformationHandler.java +++ b/src/com/android/launcher3/testing/TestInformationHandler.java @@ -201,6 +201,13 @@ public class TestInformationHandler implements ResourceBasedOverride { }); } + case TestProtocol.REQUEST_WORKSPACE_COLUMNS_ROWS: { + return getLauncherUIProperty(Bundle::putParcelable, launcher -> new Point( + InvariantDeviceProfile.INSTANCE.get(mContext).numColumns, + InvariantDeviceProfile.INSTANCE.get(mContext).numRows) + ); + } + case TestProtocol.REQUEST_HOTSEAT_CELL_CENTER: { final HotseatCellCenterRequest request = extra.getParcelable( TestProtocol.TEST_INFO_REQUEST_FIELD); diff --git a/src/com/android/launcher3/testing/shared/TestProtocol.java b/src/com/android/launcher3/testing/shared/TestProtocol.java index f5ee91b297..9b2ce9a67f 100644 --- a/src/com/android/launcher3/testing/shared/TestProtocol.java +++ b/src/com/android/launcher3/testing/shared/TestProtocol.java @@ -123,6 +123,7 @@ public final class TestProtocol { public static final String REQUEST_WORKSPACE_CELL_LAYOUT_SIZE = "workspace-cell-layout-size"; public static final String REQUEST_WORKSPACE_CELL_CENTER = "workspace-cell-center"; + public static final String REQUEST_WORKSPACE_COLUMNS_ROWS = "workspace-columns-rows"; public static final String REQUEST_HOTSEAT_CELL_CENTER = "hotseat-cell-center"; diff --git a/src/com/android/launcher3/testing/shared/WorkspaceCellCenterRequest.java b/src/com/android/launcher3/testing/shared/WorkspaceCellCenterRequest.java index 80dbef86b9..e2cd8ea965 100644 --- a/src/com/android/launcher3/testing/shared/WorkspaceCellCenterRequest.java +++ b/src/com/android/launcher3/testing/shared/WorkspaceCellCenterRequest.java @@ -124,7 +124,7 @@ public class WorkspaceCellCenterRequest implements TestInformationRequest { * Set span Height in cells */ public WorkspaceCellCenterRequest.Builder setSpanY(int y) { - this.mCellY = y; + this.mSpanY = y; return this; } diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java index 469d79f74e..6d75180f8c 100644 --- a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java +++ b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java @@ -29,7 +29,73 @@ import java.util.Set; import java.util.stream.Collectors; -public class CellLayoutBoard { +public class CellLayoutBoard implements Comparable { + + private boolean intersects(Rect r1, Rect r2) { + // If one rectangle is on left side of other + if (r1.left > r2.right || r2.left > r1.right) { + return false; + } + + // If one rectangle is above other + if (r1.bottom > r2.top || r2.bottom > r1.top) { + return false; + } + + return true; + } + + private boolean overlapsWithIgnored(Set ignoredRectangles, Rect rect) { + for (Rect ignoredRect : ignoredRectangles) { + // Using the built in intersects doesn't work because it doesn't account for area 0 + if (intersects(ignoredRect, rect)) { + return true; + } + } + return false; + } + + @Override + public int compareTo(CellLayoutBoard cellLayoutBoard) { + // to be equal they need to have the same number of widgets and the same dimensions + // their order can be different + Set widgetsSet = new HashSet<>(); + Set ignoredRectangles = new HashSet<>(); + for (WidgetRect rect : mWidgetsRects) { + if (rect.shouldIgnore()) { + ignoredRectangles.add(rect.mBounds); + } else { + widgetsSet.add(rect.mBounds); + } + } + for (WidgetRect rect : cellLayoutBoard.mWidgetsRects) { + // ignore rectangles overlapping with the area marked by x + if (overlapsWithIgnored(ignoredRectangles, rect.mBounds)) { + continue; + } + if (!widgetsSet.contains(rect.mBounds)) { + return -1; + } + widgetsSet.remove(rect.mBounds); + } + if (!widgetsSet.isEmpty()) { + return 1; + } + + // to be equal they need to have the same number of icons their order can be different + Set iconsSet = new HashSet<>(); + mIconPoints.forEach(icon -> iconsSet.add(icon.getCoord())); + for (IconPoint icon : cellLayoutBoard.mIconPoints) { + if (!iconsSet.contains(icon.getCoord())) { + return -1; + } + iconsSet.remove(icon.getCoord()); + } + if (!iconsSet.isEmpty()) { + return 1; + } + return 0; + } public static class CellType { // The cells marked by this will be filled by 1x1 widgets and will be ignored when @@ -115,7 +181,7 @@ public class CellLayoutBoard { List mIconPoints = new ArrayList<>(); Map mIconsMap = new HashMap<>(); - Point mMain = new Point(); + WidgetRect mMain = null; CellLayoutBoard() { for (int x = 0; x < mWidget.length; x++) { @@ -133,7 +199,7 @@ public class CellLayoutBoard { return mIconPoints; } - public Point getMain() { + public WidgetRect getMain() { return mMain; } @@ -273,6 +339,16 @@ public class CellLayoutBoard { return iconPoints; } + public static WidgetRect getMainFromList(List boards) { + for (CellLayoutBoard board : boards) { + WidgetRect main = board.getMain(); + if (main != null) { + return main; + } + } + return null; + } + public static CellLayoutBoard boardFromString(String boardStr) { String[] lines = boardStr.split("\n"); CellLayoutBoard board = new CellLayoutBoard(); @@ -281,17 +357,18 @@ public class CellLayoutBoard { String line = lines[y]; for (int x = 0; x < line.length(); x++) { char c = line.charAt(x); - if (c == CellType.MAIN_WIDGET) { - board.mMain = new Point(x, y); - } if (c != CellType.EMPTY) { board.mWidget[x][y] = line.charAt(x); } } } board.mWidgetsRects = getRects(board.mWidget); - board.mWidgetsRects.forEach( - widgetRect -> board.mWidgetsMap.put(widgetRect.mType, widgetRect)); + board.mWidgetsRects.forEach(widgetRect -> { + if (widgetRect.mType == CellType.MAIN_WIDGET) { + board.mMain = widgetRect; + } + board.mWidgetsMap.put(widgetRect.mType, widgetRect); + }); board.mIconPoints = getIconPoints(board.mWidget); return board; } @@ -308,4 +385,24 @@ public class CellLayoutBoard { } return s.toString(); } + + public static List boardListFromString(String boardsStr) { + String[] lines = boardsStr.split("\n"); + ArrayList individualBoards = new ArrayList<>(); + ArrayList boards = new ArrayList<>(); + for (String line : lines) { + String[] boardSegment = line.split("\\|"); + for (int i = 0; i < boardSegment.length; i++) { + if (i >= individualBoards.size()) { + individualBoards.add(boardSegment[i]); + } else { + individualBoards.set(i, individualBoards.get(i) + "\n" + boardSegment[i]); + } + } + } + for (String board : individualBoards) { + boards.add(CellLayoutBoard.boardFromString(board)); + } + return boards; + } } diff --git a/tests/src/com/android/launcher3/celllayout/MultipleCellLayoutsSimpleReorder.java b/tests/src/com/android/launcher3/celllayout/MultipleCellLayoutsSimpleReorder.java new file mode 100644 index 0000000000..9b52fe826c --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/MultipleCellLayoutsSimpleReorder.java @@ -0,0 +1,88 @@ +/* + * 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.testcases; + +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 MultipleCellLayoutsSimpleReorder { + + /** 5x5 Test + **/ + private static final String START_BOARD_STR_5x5 = "" + + "xxxxx|-----\n" + + "--mm-|-----\n" + + "--mm-|-----\n" + + "-----|-----\n" + + "-----|-----"; + private static final Point MOVE_TO_5x5 = new Point(8, 3); + private static final String END_BOARD_STR_5x5 = "" + + "xxxxx|-----\n" + + "-----|-----\n" + + "-----|-----\n" + + "-----|---mm\n" + + "-----|---mm"; + private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_STR_5x5, + MOVE_TO_5x5, + END_BOARD_STR_5x5); + + /** 4x4 Test + **/ + private static final String START_BOARD_STR_4x4 = "" + + "xxxx|----\n" + + "--mm|----\n" + + "--mm|----\n" + + "----|----"; + private static final Point MOVE_TO_4x4 = new Point(5, 3); + private static final String END_BOARD_STR_4x4 = "" + + "xxxx|----\n" + + "----|----\n" + + "----|-mm-\n" + + "----|-mm-"; + private static final ReorderTestCase TEST_CASE_4x4 = new ReorderTestCase(START_BOARD_STR_4x4, + MOVE_TO_4x4, + END_BOARD_STR_4x4); + + + /** 6x5 Test + **/ + private static final String START_BOARD_STR_6x5 = "" + + "xxxxxx|------\n" + + "--m---|------\n" + + "------|------\n" + + "------|------\n" + + "------|------"; + private static final Point MOVE_TO_6x5 = new Point(10, 4); + private static final String END_BOARD_STR_6x5 = "" + + "xxxxxx|------\n" + + "------|------\n" + + "------|------\n" + + "------|------\n" + + "------|----m-"; + private static final ReorderTestCase TEST_CASE_6x5 = new ReorderTestCase(START_BOARD_STR_6x5, + MOVE_TO_6x5, + END_BOARD_STR_6x5); + + public static final Map TEST_BY_GRID_SIZE = + Map.of(new Point(5, 5), TEST_CASE_5x5, + new Point(4, 4), TEST_CASE_4x4, + new Point(6, 5), TEST_CASE_6x5); +} diff --git a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java index 843f0112ba..92fe9a6099 100644 --- a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java +++ b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java @@ -29,6 +29,7 @@ import com.android.launcher3.CellLayout; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.celllayout.testcases.FullReorderCase; import com.android.launcher3.celllayout.testcases.MoveOutReorderCase; +import com.android.launcher3.celllayout.testcases.MultipleCellLayoutsSimpleReorder; import com.android.launcher3.celllayout.testcases.PushReorderCase; import com.android.launcher3.celllayout.testcases.ReorderTestCase; import com.android.launcher3.celllayout.testcases.SimpleReorderCase; @@ -38,7 +39,6 @@ import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.TaplTestsLauncher3; import com.android.launcher3.util.rule.ShellCommandRule; import com.android.launcher3.views.DoubleShadowBubbleTextView; -import com.android.launcher3.widget.LauncherAppWidgetHostView; import org.junit.Assume; import org.junit.Before; @@ -48,6 +48,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; @@ -62,20 +63,6 @@ public class ReorderWidgets extends AbstractLauncherUiTest { TestWorkspaceBuilder mWorkspaceBuilder; - private View getViewAt(int cellX, int cellY) { - return getFromLauncher(l -> l.getWorkspace().getScreenWithId( - l.getWorkspace().getScreenIdForPageIndex(0)).getChildAt(cellX, cellY)); - } - - private Point getCellDimensions() { - return getFromLauncher(l -> { - CellLayout cellLayout = l.getWorkspace().getScreenWithId( - l.getWorkspace().getScreenIdForPageIndex(0)); - return new Point(cellLayout.getWidth() / cellLayout.getCountX(), - cellLayout.getHeight() / cellLayout.getCountY()); - }); - } - @Before public void setup() throws Throwable { mWorkspaceBuilder = new TestWorkspaceBuilder(this, mTargetContext); @@ -86,28 +73,26 @@ public class ReorderWidgets extends AbstractLauncherUiTest { /** * Validate if the given board represent the current CellLayout **/ - private boolean validateBoard(CellLayoutBoard board) { - boolean match = true; - Point cellDimensions = getCellDimensions(); - for (CellLayoutBoard.WidgetRect widgetRect : board.getWidgets()) { - if (widgetRect.shouldIgnore()) { - continue; + private boolean validateBoard(List testBoards) { + ArrayList workspaceBoards = workspaceToBoards(); + if (workspaceBoards.size() < testBoards.size()) { + return false; + } + for (int i = 0; i < testBoards.size(); i++) { + if (testBoards.get(i).compareTo(workspaceBoards.get(i)) != 0) { + return false; } - 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 true; + } + + private FavoriteItemsTransaction buildWorkspaceFromBoards(List boards, + FavoriteItemsTransaction transaction) { + for (int i = 0; i < boards.size(); i++) { + CellLayoutBoard board = boards.get(i); + mWorkspaceBuilder.buildFromBoard(board, transaction, i); } - return match; + return transaction; } private void printCurrentWorkspace() { @@ -148,22 +133,25 @@ public class ReorderWidgets extends AbstractLauncherUiTest { private void runTestCase(ReorderTestCase testCase) throws ExecutionException, InterruptedException { - Point mainWidgetCellPos = testCase.mStart.getMain(); + CellLayoutBoard.WidgetRect mainWidgetCellPos = CellLayoutBoard.getMainFromList( + testCase.mStart); FavoriteItemsTransaction transaction = new FavoriteItemsTransaction(mTargetContext, this); - mWorkspaceBuilder.buildFromBoard(testCase.mStart, transaction).commit(); + transaction = buildWorkspaceFromBoards(testCase.mStart, transaction); + transaction.commit(); waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading()); - Widget widget = mLauncher.getWorkspace().getWidgetAtCell(mainWidgetCellPos.x, - mainWidgetCellPos.y); + Widget widget = mLauncher.getWorkspace().getWidgetAtCell(mainWidgetCellPos.getCellX(), + mainWidgetCellPos.getCellY()); assertNotNull(widget); WidgetResizeFrame resizeFrame = widget.dragWidgetToWorkspace(testCase.moveMainTo.x, - testCase.moveMainTo.y); + testCase.moveMainTo.y, mainWidgetCellPos.getSpanX(), mainWidgetCellPos.getSpanY()); resizeFrame.dismiss(); boolean isValid = false; - for (CellLayoutBoard board : testCase.mEnd) { - isValid |= validateBoard(board); + for (List boards : testCase.mEnd) { + isValid |= validateBoard(boards); + if (isValid) break; } printCurrentWorkspace(); assertTrue("Non of the valid boards match with the current state", isValid); @@ -207,4 +195,11 @@ public class ReorderWidgets extends AbstractLauncherUiTest { runTestCaseMap(MoveOutReorderCase.TEST_BY_GRID_SIZE, MoveOutReorderCase.class.getSimpleName()); } + + @Test + public void multipleCellLayoutsSimpleReorder() throws ExecutionException, InterruptedException { + Assume.assumeTrue("Test doesn't support foldables", !mLauncher.isTwoPanels()); + runTestCaseMap(MultipleCellLayoutsSimpleReorder.TEST_BY_GRID_SIZE, + MultipleCellLayoutsSimpleReorder.class.getSimpleName()); + } } diff --git a/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java b/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java index 16448afda0..0a38ed7eee 100644 --- a/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java +++ b/tests/src/com/android/launcher3/celllayout/TestWorkspaceBuilder.java @@ -15,7 +15,6 @@ */ package com.android.launcher3.celllayout; -import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID; import static com.android.launcher3.util.WidgetUtils.createWidgetInfo; import android.content.ComponentName; @@ -62,7 +61,7 @@ public class TestWorkspaceBuilder { * Fills the given rect in WidgetRect with 1x1 widgets. This is useful to equalize cases. */ private FavoriteItemsTransaction fillWithWidgets(CellLayoutBoard.WidgetRect widgetRect, - FavoriteItemsTransaction transaction) { + FavoriteItemsTransaction transaction, int screenId) { int initX = widgetRect.getCellX(); int initY = widgetRect.getCellY(); for (int x = initX; x < initX + widgetRect.getSpanX(); x++) { @@ -71,8 +70,7 @@ public class TestWorkspaceBuilder { // this widgets are filling, we don't care if we can't place them ItemInfo item = createWidgetInCell( new CellLayoutBoard.WidgetRect(CellLayoutBoard.CellType.IGNORE, - new Rect(x, y, x, y)) - ); + new Rect(x, y, x, y)), screenId); transaction.addItem(item); } catch (Exception e) { Log.d(TAG, "Unable to place filling widget at " + x + "," + y); @@ -94,11 +92,11 @@ public class TestWorkspaceBuilder { } private void addCorrespondingWidgetRect(CellLayoutBoard.WidgetRect widgetRect, - FavoriteItemsTransaction transaction) { + FavoriteItemsTransaction transaction, int screenId) { if (widgetRect.mType == 'x') { - fillWithWidgets(widgetRect, transaction); + fillWithWidgets(widgetRect, transaction, screenId); } else { - transaction.addItem(createWidgetInCell(widgetRect)); + transaction.addItem(createWidgetInCell(widgetRect, screenId)); } } @@ -106,11 +104,11 @@ public class TestWorkspaceBuilder { * Builds the given board into the transaction */ public FavoriteItemsTransaction buildFromBoard(CellLayoutBoard board, - FavoriteItemsTransaction transaction) { + FavoriteItemsTransaction transaction, final int screenId) { board.getWidgets().forEach( - (widgetRect) -> addCorrespondingWidgetRect(widgetRect, transaction)); + (widgetRect) -> addCorrespondingWidgetRect(widgetRect, transaction, screenId)); board.getIcons().forEach((iconPoint) -> - transaction.addItem(createIconInCell(iconPoint)) + transaction.addItem(createIconInCell(iconPoint, screenId)) ); return transaction; } @@ -127,7 +125,7 @@ public class TestWorkspaceBuilder { return transaction; } - private ItemInfo createWidgetInCell(CellLayoutBoard.WidgetRect widgetRect) { + private ItemInfo createWidgetInCell(CellLayoutBoard.WidgetRect widgetRect, int screenId) { LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(mTest, false); LauncherAppWidgetInfo item = createWidgetInfo(info, ApplicationProvider.getApplicationContext(), true); @@ -136,14 +134,14 @@ public class TestWorkspaceBuilder { item.cellY = widgetRect.getCellY(); item.spanX = widgetRect.getSpanX(); item.spanY = widgetRect.getSpanY(); - item.screenId = FIRST_SCREEN_ID; + item.screenId = screenId; return item; } - private ItemInfo createIconInCell(CellLayoutBoard.IconPoint iconPoint) { + private ItemInfo createIconInCell(CellLayoutBoard.IconPoint iconPoint, int screenId) { WorkspaceItemInfo item = new WorkspaceItemInfo(getApp()); item.id = getID(); - item.screenId = FIRST_SCREEN_ID; + item.screenId = screenId; item.cellX = iconPoint.getCoord().x; item.cellY = iconPoint.getCoord().y; item.minSpanY = item.minSpanX = item.spanX = item.spanY = 1; diff --git a/tests/src/com/android/launcher3/celllayout/testcases/ReorderTestCase.java b/tests/src/com/android/launcher3/celllayout/testcases/ReorderTestCase.java index 0a28668d96..1689e47834 100644 --- a/tests/src/com/android/launcher3/celllayout/testcases/ReorderTestCase.java +++ b/tests/src/com/android/launcher3/celllayout/testcases/ReorderTestCase.java @@ -24,23 +24,23 @@ import java.util.List; import java.util.stream.Collectors; public class ReorderTestCase { - public CellLayoutBoard mStart; + public List mStart; public Point moveMainTo; - public List mEnd; + public List> mEnd; - ReorderTestCase(CellLayoutBoard start, Point moveMainTo, CellLayoutBoard ... end) { + ReorderTestCase(List start, Point moveMainTo, List ... end) { mStart = start; this.moveMainTo = moveMainTo; mEnd = Arrays.asList(end); } ReorderTestCase(String start, Point moveMainTo, String ... end) { - mStart = CellLayoutBoard.boardFromString(start); + mStart = CellLayoutBoard.boardListFromString(start); this.moveMainTo = moveMainTo; mEnd = Arrays .asList(end) .stream() - .map(CellLayoutBoard::boardFromString) + .map(CellLayoutBoard::boardListFromString) .collect(Collectors.toList()); } } diff --git a/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java index 546c48bccb..c9f6849854 100644 --- a/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java +++ b/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java @@ -33,13 +33,13 @@ public class SimpleReorderCase { + "--mm-\n" + "-----\n" + "-----"; - private static final Point MOVE_TO_5x5 = new Point(4, 4); + private static final Point MOVE_TO_5x5 = new Point(0, 4); private static final String END_BOARD_STR_5x5 = "" + "xxxxx\n" + "-----\n" + "-----\n" - + "---mm\n" - + "---mm"; + + "mm---\n" + + "mm---"; private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_STR_5x5, MOVE_TO_5x5, END_BOARD_STR_5x5); @@ -61,7 +61,27 @@ public class SimpleReorderCase { MOVE_TO_4x4, END_BOARD_STR_4x4); + /** 6x5 Test + **/ + private static final String START_BOARD_STR_6x5 = "" + + "xxxxxx\n" + + "-mm---\n" + + "-mm---\n" + + "------\n" + + "------"; + private static final Point MOVE_TO_6x5 = new Point(4, 3); + private static final String END_BOARD_STR_6x5 = "" + + "xxxxxx\n" + + "------\n" + + "------\n" + + "----mm\n" + + "----mm"; + private static final ReorderTestCase TEST_CASE_6x5 = new ReorderTestCase(START_BOARD_STR_6x5, + MOVE_TO_6x5, + END_BOARD_STR_6x5); + public static final Map TEST_BY_GRID_SIZE = Map.of(new Point(5, 5), TEST_CASE_5x5, - new Point(4, 4), TEST_CASE_4x4); + new Point(4, 4), TEST_CASE_4x4, + new Point(6, 5), TEST_CASE_6x5); } diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 70d122bc90..cf21dd723f 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -299,7 +299,7 @@ public abstract class AbstractLauncherUiTest { /** * Removes all icons from homescreen and hotseat. */ - public void clearHomescreen() throws Throwable { + public void clearHomescreen() { LauncherSettings.Settings.call(mTargetContext.getContentResolver(), LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); LauncherSettings.Settings.call(mTargetContext.getContentResolver(), diff --git a/tests/tapl/com/android/launcher3/tapl/Widget.java b/tests/tapl/com/android/launcher3/tapl/Widget.java index 046308b595..d440903eb6 100644 --- a/tests/tapl/com/android/launcher3/tapl/Widget.java +++ b/tests/tapl/com/android/launcher3/tapl/Widget.java @@ -71,7 +71,7 @@ public final class Widget extends Launchable implements WorkspaceDragSource { public WidgetResizeFrame dragWidgetToWorkspace() { try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) { return dragWidgetToWorkspace(/* configurable= */ false, /* acceptsConfig= */ false, -1, - -1); + -1, 1, 1); } } @@ -80,12 +80,12 @@ public final class Widget extends Launchable implements WorkspaceDragSource { * cellY and returns the resize frame that is shown after the widget is added. */ @NonNull - public WidgetResizeFrame dragWidgetToWorkspace(int cellX, int cellY) { + public WidgetResizeFrame dragWidgetToWorkspace(int cellX, int cellY, int spanX, int spanY) { try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "Dragging widget to workspace cell " + cellX + "," + cellY)) { return dragWidgetToWorkspace(/* configurable= */ false, /* acceptsConfig= */ false, - cellX, cellY); + cellX, cellY, spanX, spanY); } } @@ -98,7 +98,7 @@ public final class Widget extends Launchable implements WorkspaceDragSource { public WidgetResizeFrame dragConfigWidgetToWorkspace(boolean acceptsConfig) { // TODO(b/239438337, fransebas) add correct event checking for this case //try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) { - return dragWidgetToWorkspace(/* configurable= */ true, acceptsConfig, -1, -1); + return dragWidgetToWorkspace(/* configurable= */ true, acceptsConfig, -1, -1, 1, 1); //} } @@ -110,18 +110,17 @@ public final class Widget extends Launchable implements WorkspaceDragSource { * @param cellX X position in the CellLayout * @param cellY Y position in the CellLayout */ - private void dragToWorkspace(boolean startsActivity, boolean isWidgetShortcut, int cellX, - int cellY) { + private void dragToWorkspaceCellPosition(boolean startsActivity, boolean isWidgetShortcut, + int cellX, int cellY, int spanX, int spanY) { Launchable launchable = getLaunchable(); LauncherInstrumentation launcher = launchable.mLauncher; - Workspace.dragIconToWorkspace( + Workspace.dragIconToWorkspaceCellPosition( launcher, launchable, - () -> Workspace.getCellCenter(launchable.mLauncher, cellX, cellY), + cellX, cellY, spanX, spanY, startsActivity, isWidgetShortcut, launchable::addExpectedEventsForLongClick); - } /** @@ -144,13 +143,13 @@ public final class Widget extends Launchable implements WorkspaceDragSource { */ @Nullable private WidgetResizeFrame dragWidgetToWorkspace(boolean configurable, boolean acceptsConfig, - int cellX, int cellY) { + int cellX, int cellY, int spanX, int spanY) { if (cellX == -1 || cellY == -1) { internalDragToWorkspace(/* startsActivity= */ configurable, /* isWidgetShortcut= */ false); } else { - dragToWorkspace(/* startsActivity= */ configurable, /* isWidgetShortcut= */ false, - cellX, cellY); + dragToWorkspaceCellPosition(/* startsActivity= */ configurable, /* isWidgetShortcut= */ + false, cellX, cellY, spanX, spanY); } if (configurable) { diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index 2c82c50eca..473cfb9cb8 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -408,9 +408,15 @@ public final class Workspace extends Home { } static Point getCellCenter(LauncherInstrumentation launcher, int cellX, int cellY) { - return launcher.getTestInfo(WorkspaceCellCenterRequest.builder().setCellX( - cellX).setCellY(cellY).build()).getParcelable( - TestProtocol.TEST_INFO_RESPONSE_FIELD); + return launcher.getTestInfo(WorkspaceCellCenterRequest.builder().setCellX(cellX).setCellY( + cellY).build()).getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD); + } + + static Point getCellCenter(LauncherInstrumentation launcher, int cellX, int cellY, int spanX, + int spanY) { + return launcher.getTestInfo(WorkspaceCellCenterRequest.builder().setCellX(cellX) + .setCellY(cellY).setSpanX(spanX).setSpanY(spanY).build()) + .getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD); } static Point getHotseatCellCenter(LauncherInstrumentation launcher, int cellInd) { @@ -418,6 +424,12 @@ public final class Workspace extends Home { .setCellInd(cellInd).build()).getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD); } + /** Returns the number of rows and columns in the workspace */ + public Point getRowsAndCols() { + return mLauncher.getTestInfo(TestProtocol.REQUEST_WORKSPACE_COLUMNS_ROWS).getParcelable( + TestProtocol.TEST_INFO_RESPONSE_FIELD); + } + /** * Finds folder icons in the current workspace. * @@ -456,6 +468,19 @@ public final class Workspace extends Home { launcher, launchable, dest, expectLongClickEvents, expectDropEvents); } + static void dragIconToWorkspaceCellPosition(LauncherInstrumentation launcher, + Launchable launchable, int cellX, int cellY, int spanX, int spanY, + boolean startsActivity, boolean isWidgetShortcut, Runnable expectLongClickEvents) { + Runnable expectDropEvents = null; + if (startsActivity || isWidgetShortcut) { + expectDropEvents = () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, + LauncherInstrumentation.EVENT_START); + } + dragIconToWorkspaceCellPosition( + launcher, launchable, cellX, cellY, spanX, spanY, true, expectLongClickEvents, + expectDropEvents); + } + /** * Drag icon in workspace to else where and drop it immediately. * (There is no slow down time before drop event) @@ -525,6 +550,51 @@ public final class Workspace extends Home { } } + static void dragIconToWorkspaceCellPosition( + LauncherInstrumentation launcher, + Launchable launchable, + int cellX, int cellY, int spanX, int spanY, + boolean isDecelerating, + Runnable expectLongClickEvents, + @Nullable Runnable expectDropEvents) { + try (LauncherInstrumentation.Closable ignored = launcher.addContextLayer( + "want to drag icon to workspace")) { + Point rowsAndCols = launcher.getWorkspace().getRowsAndCols(); + int destinationWorkspace = cellX / rowsAndCols.x; + cellX = cellX % rowsAndCols.x; + + final long downTime = SystemClock.uptimeMillis(); + Point dragStart = launchable.startDrag( + downTime, + expectLongClickEvents, + /* runToSpringLoadedState= */ true); + Point targetDest = getCellCenter(launcher, cellX, cellY, spanX, spanY); + int displayX = launcher.getRealDisplaySize().x; + + // Since the destination can be on another page, we need to drag to the edge first + // until we reach the target page + for (int i = 0; i < destinationWorkspace; i++) { + // Don't drag all the way to the edge to prevent touch events from getting out of + //screen bounds. + Point screenEdge = new Point(displayX - 1, targetDest.y); + Point finalDragStart = dragStart; + executeAndWaitForPageScroll(launcher, + () -> launcher.movePointer(finalDragStart, screenEdge, DEFAULT_DRAG_STEPS, + true, downTime, downTime, true, + LauncherInstrumentation.GestureScope.INSIDE)); + dragStart = screenEdge; + } + + // targetDest.x is now between 0 and displayX so we found the target page, + // we just have to put move the icon to the destination and drop it + launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, isDecelerating, + downTime, SystemClock.uptimeMillis(), false, + LauncherInstrumentation.GestureScope.INSIDE); + launcher.runCallbackIfActive(CALLBACK_HOLD_BEFORE_DROP); + dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents); + } + } + private static void executeAndWaitForPageScroll(LauncherInstrumentation launcher, Runnable command) { launcher.executeAndWaitForEvent(command, From 85d664efaa668f272895ca45b9f2f2b1197cae33 Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Mon, 16 Jan 2023 16:31:59 +0000 Subject: [PATCH 050/469] Use transient taskbar size/margin to calculate taskbar position for persistent taskbar - DeviceProfileTest should use persistent taskbar in 3-button mode Fix: 260596114 Test: DeviceProfileTest Change-Id: Iadcca218dab2bc2eedc006c86dbbe3f9a0fa5e51 --- src/com/android/launcher3/DeviceProfile.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index f124940e3d..7b540d00c2 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -260,6 +260,7 @@ public class DeviceProfile { // Whether Taskbar will inset the bottom of apps by taskbarSize. public boolean isTaskbarPresentInApps; public int taskbarSize; + public int transientTaskbarSize; public int stashedTaskbarSize; public int transientTaskbarMargin; @@ -324,12 +325,12 @@ public class DeviceProfile { } if (isTaskbarPresent) { + transientTaskbarSize = res.getDimensionPixelSize(R.dimen.transient_taskbar_size); + transientTaskbarMargin = res.getDimensionPixelSize(R.dimen.transient_taskbar_margin); if (DisplayController.isTransientTaskbar(context)) { - taskbarSize = res.getDimensionPixelSize(R.dimen.transient_taskbar_size); + taskbarSize = transientTaskbarSize; stashedTaskbarSize = res.getDimensionPixelSize(R.dimen.transient_taskbar_stashed_size); - transientTaskbarMargin = - res.getDimensionPixelSize(R.dimen.transient_taskbar_margin); } else { taskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_size); stashedTaskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_stashed_size); @@ -1383,7 +1384,7 @@ public class DeviceProfile { public int getOverviewActionsClaimedSpaceBelow() { if (isTaskbarPresent) { if (FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) { - return taskbarSize + transientTaskbarMargin * 2; + return transientTaskbarSize + transientTaskbarMargin * 2; } return isGestureMode From 835ccc58e04cf5e9b2a1a9afe0b3e4552be95398 Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Mon, 16 Jan 2023 18:20:27 +0000 Subject: [PATCH 051/469] Close Taskbar AllApps when going from overview to home - Also trigger closeAllOpenViews when changing state to NORMAL Fix: 265689318 Test: manual Change-Id: I109c0f3a3c741844c8747bd76a6c38bf301ec737 --- .../taskbar/TaskbarLauncherStateController.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index 4ad3858de1..3496b2b049 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -258,8 +258,9 @@ import java.util.StringJoiner; } private Animator onStateChangeApplied(int changedFlags, long duration, boolean start) { - boolean goingToLauncher = isInLauncher(); + final boolean goingToLauncher = isInLauncher(); final float toAlignment = isIconAlignedWithHotseat() ? 1 : 0; + boolean handleOpenFloatingViews = false; if (DEBUG) { Log.d(TAG, "onStateChangeApplied - mState: " + getStateString(mState) + ", changedFlags: " + getStateString(changedFlags) @@ -280,6 +281,10 @@ import java.util.StringJoiner; updateStateForFlag(FLAG_RESUMED, false); applyState(0 /* duration */); } + if (mLauncherState == LauncherState.NORMAL) { + // We're changing state to home, should close open popups e.g. Taskbar AllApps + handleOpenFloatingViews = true; + } } if (hasAnyFlag(changedFlags, FLAGS_LAUNCHER)) { @@ -303,10 +308,11 @@ import java.util.StringJoiner; } }); - if (goingToLauncher) { - // Handle closing open popups when going home/overview - AbstractFloatingView.closeAllOpenViews(mControllers.taskbarActivityContext); - } + // Handle closing open popups when going home/overview + handleOpenFloatingViews = true; + } + if (handleOpenFloatingViews && goingToLauncher) { + AbstractFloatingView.closeAllOpenViews(mControllers.taskbarActivityContext); } float backgroundAlpha = From 2842bc72fd759bc72387304a6d28da44ce0b1fca Mon Sep 17 00:00:00 2001 From: Jeremy Sim Date: Fri, 6 Jan 2023 16:33:51 -0800 Subject: [PATCH 052/469] Fix bug with Taskbar launches in Overview This patch fixes a bug where Taskbar launches (tapping an icon on the Taskbar) were not executing correctly in Overview. Now that Taskbar is always present in Overview, we need to handle cases where the user taps to launch an app, but the app is already visible to the user in Overview. This was breaking in a noticeable way with split apps, where the Taskbar simply wouldn't respond when the tapped app was already visible as a live tile. Fixed by polling RecentsModel for already-running tasks, checking to see if the associated TaskView is visible to the user or not, and calling launchTasks() on the TaskView if so. If the tile is not visible to the user, the app will launch normally. Fixes: 261952204 Test: Work in progress Change-Id: If761546913bde7451a22456a272ba6c31942c5f8 --- .../popup/QuickstepSystemShortcut.java | 2 +- .../taskbar/TaskbarActivityContext.java | 39 ++++++++++++++++--- .../taskbar/TaskbarUIController.java | 2 +- .../android/quickstep/views/RecentsView.java | 8 ++-- 4 files changed, 39 insertions(+), 12 deletions(-) diff --git a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java index 9554bd3d9c..45d1b11c56 100644 --- a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java +++ b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java @@ -101,7 +101,7 @@ public interface QuickstepSystemShortcut { RecentsView recentsView = mTarget.getOverviewPanel(); // Check if there is already an instance of this app running, if so, initiate the split // using that. - recentsView.findLastActiveTaskAndDoSplitOperation( + recentsView.findLastActiveTaskAndRunCallback( intent.getComponent(), (Consumer) foundTask -> { SplitSelectSource source = new SplitSelectSource(mOriginalView, diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 731eea78e9..ba1cf9e1d1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -94,6 +94,7 @@ import com.android.launcher3.util.TraceHelper; import com.android.launcher3.util.ViewCache; import com.android.launcher3.views.ActivityContext; import com.android.quickstep.views.RecentsView; +import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.rotation.RotationButtonController; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -101,6 +102,7 @@ import com.android.systemui.unfold.updates.RotationChangeProvider; import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider; import java.io.PrintWriter; +import java.util.function.Consumer; /** * The {@link ActivityContext} with which we inflate Taskbar-related Views. This allows UI elements @@ -781,12 +783,12 @@ public class TaskbarActivityContext extends BaseTaskbarContext { }); }); } else if (tag instanceof WorkspaceItemInfo) { + // Tapping a launchable icon on Taskbar WorkspaceItemInfo info = (WorkspaceItemInfo) tag; if (!info.isDisabled() || !ItemClickHandler.handleDisabledItemClicked(info, this)) { TaskbarUIController taskbarUIController = mControllers.uiController; RecentsView recents = taskbarUIController.getRecentsView(); - if (recents != null - && taskbarUIController.getRecentsView().isSplitSelectionActive()) { + if (recents != null && recents.isSplitSelectionActive()) { // If we are selecting a second app for split, launch the split tasks taskbarUIController.triggerSecondAppForSplit(info, info.intent, view); } else { @@ -813,7 +815,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { getSystemService(LauncherApps.class) .startShortcut(packageName, id, null, null, info.user); } else { - startItemInfoActivity(info); + launchFromTaskbarPreservingSplitIfVisible(recents, info); } mControllers.uiController.onTaskbarIconLaunched(info); @@ -828,6 +830,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true); } } else if (tag instanceof AppInfo) { + // Tapping an item in AllApps AppInfo info = (AppInfo) tag; TaskbarUIController taskbarUIController = mControllers.uiController; RecentsView recents = taskbarUIController.getRecentsView(); @@ -836,9 +839,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext { // If we are selecting a second app for split, launch the split tasks taskbarUIController.triggerSecondAppForSplit(info, info.intent, view); } else { - // Else launch the selected task - startItemInfoActivity((AppInfo) tag); - mControllers.uiController.onTaskbarIconLaunched((AppInfo) tag); + launchFromTaskbarPreservingSplitIfVisible(recents, info); + mControllers.uiController.onTaskbarIconLaunched(info); } mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true); } else if (tag instanceof ItemClickProxy) { @@ -850,6 +852,31 @@ public class TaskbarActivityContext extends BaseTaskbarContext { AbstractFloatingView.closeAllOpenViews(this); } + /** + * Run when the user taps a Taskbar icon while in Overview. If the tapped app is currently + * visible to the user in Overview, or is part of a visible split pair, we expand the TaskView + * as if the user tapped on it (preserving the split pair). Otherwise, launch it normally + * (potentially breaking a split pair). + */ + private void launchFromTaskbarPreservingSplitIfVisible(RecentsView recents, ItemInfo info) { + recents.findLastActiveTaskAndRunCallback( + info.getTargetComponent(), + (Consumer) foundTask -> { + if (foundTask != null) { + TaskView foundTaskView = + recents.getTaskViewByTaskId(foundTask.key.id); + if (foundTaskView != null + && foundTaskView.isVisibleToUser()) { + TestLogging.recordEvent( + TestProtocol.SEQUENCE_MAIN, "start: taskbarAppIcon"); + foundTaskView.launchTasks(); + return; + } + } + startItemInfoActivity(info); + }); + } + private void startItemInfoActivity(ItemInfo info) { Intent intent = new Intent(info.getIntent()) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java index 7b0374614c..ebb37a88fc 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java @@ -170,7 +170,7 @@ public class TaskbarUIController { */ public void triggerSecondAppForSplit(ItemInfoWithIcon info, Intent intent, View startingView) { RecentsView recents = getRecentsView(); - recents.findLastActiveTaskAndDoSplitOperation( + recents.findLastActiveTaskAndRunCallback( info.getTargetComponent(), (Consumer) foundTask -> { if (foundTask != null) { diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 5e645ea917..8ac4251951 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -1275,14 +1275,14 @@ public abstract class RecentsView callback) { mModel.getTasks(taskGroups -> { Task lastActiveTask = null; From cb7bd50247beae0c8298bfe01330b46589c5e354 Mon Sep 17 00:00:00 2001 From: Nicolo' Mazzucato Date: Mon, 16 Jan 2023 19:46:30 +0000 Subject: [PATCH 053/469] Log device profile changes in perfetto traces This is needed to easily spot unwanted DeviceProfile changes in perfetto traces, as each one causes many new inflation and slows down the ui thread considerably. Test: recorded trace with this log Bug: 258214245 Change-Id: I805d56d4dfe1c08d7f28215c0462d01fcaece84e --- .../launcher3/taskbar/TaskbarActivityContext.java | 9 +++++++++ .../launcher3/uioverrides/QuickstepLauncher.java | 9 +++++++++ .../src/com/android/quickstep/RecentsActivity.java | 9 +++++++++ src/com/android/launcher3/DeviceProfile.java | 10 ++++++++++ 4 files changed, 37 insertions(+) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 4e795d9090..6caa904071 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -16,6 +16,7 @@ package com.android.launcher3.taskbar; import static android.content.pm.PackageManager.FEATURE_PC; +import static android.os.Trace.TRACE_TAG_APP; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; @@ -46,6 +47,7 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.Process; import android.os.SystemProperties; +import android.os.Trace; import android.provider.Settings; import android.util.Log; import android.view.Display; @@ -266,6 +268,13 @@ public class TaskbarActivityContext extends BaseTaskbarContext { dispatchDeviceProfileChanged(); } + @Override + public void dispatchDeviceProfileChanged() { + super.dispatchDeviceProfileChanged(); + Trace.instantForTrack(TRACE_TAG_APP, "TaskbarActivityContext#DeviceProfileChanged", + getDeviceProfile().toSmallString()); + } + /** * Copy the original DeviceProfile, match the number of hotseat icons and qsb width and update * the icon size diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index a7651b6a06..0114457909 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.uioverrides; +import static android.os.Trace.TRACE_TAG_APP; import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT; @@ -62,6 +63,7 @@ import android.os.Bundle; import android.os.CancellationSignal; import android.os.IBinder; import android.os.SystemProperties; +import android.os.Trace; import android.view.Display; import android.view.HapticFeedbackConstants; import android.view.RemoteAnimationTarget; @@ -1036,6 +1038,13 @@ public class QuickstepLauncher extends Launcher { return false; } + @Override + public void dispatchDeviceProfileChanged() { + super.dispatchDeviceProfileChanged(); + Trace.instantForTrack(TRACE_TAG_APP, "QuickstepLauncher#DeviceProfileChanged", + getDeviceProfile().toSmallString()); + } + private static final class LauncherTaskViewController extends TaskViewTouchController { diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java index 6f86bf5de6..f26189ce57 100644 --- a/quickstep/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/src/com/android/quickstep/RecentsActivity.java @@ -15,6 +15,7 @@ */ package com.android.quickstep; +import static android.os.Trace.TRACE_TAG_APP; import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.RemoteAnimationTarget.MODE_OPENING; @@ -36,6 +37,7 @@ import android.content.res.Configuration; import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import android.os.Trace; import android.view.Display; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; @@ -448,6 +450,13 @@ public final class RecentsActivity extends StatefulActivity { return new RecentsAtomicAnimationFactory<>(this); } + @Override + public void dispatchDeviceProfileChanged() { + super.dispatchDeviceProfileChanged(); + Trace.instantForTrack(TRACE_TAG_APP, "RecentsActivity#DeviceProfileChanged", + getDeviceProfile().toSmallString()); + } + private AnimatorListenerAdapter resetStateListener() { return new AnimatorListenerAdapter() { @Override diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index f124940e3d..e4f3614ccd 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -1679,6 +1679,16 @@ public class DeviceProfile { writer.println(prefix + pxToDpStr("getCellLayoutWidth()", getCellLayoutWidth())); } + /** Returns a reduced representation of this DeviceProfile. */ + public String toSmallString() { + return "isTablet:" + isTablet + ", " + + "isMultiDisplay:" + isMultiDisplay + ", " + + "widthPx:" + widthPx + ", " + + "heightPx:" + heightPx + ", " + + "insets:" + mInsets + ", " + + "rotationHint:" + rotationHint; + } + private static Context getContext(Context c, Info info, int orientation, WindowBounds bounds) { Configuration config = new Configuration(c.getResources().getConfiguration()); config.orientation = orientation; From 3278715fd49514432cb124c9efbf75bbb3d4a191 Mon Sep 17 00:00:00 2001 From: Federico Baron Date: Tue, 17 Jan 2023 16:42:43 -0800 Subject: [PATCH 054/469] Change preload icon size to always be 0.7 scale when downloading In order to match upcoming designs. We will keep the icon size when downloading at 0.7 scale Test: download an app with ENABLE_DOWNLOAD_APP_UX_V2 on Bug: 254858049 Change-Id: I7f22eb3a5c6f1e75f19e033ff11a70aa4010ea10 --- src/com/android/launcher3/graphics/PreloadIconDrawable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java index 9001a5270e..f2fde0e7dd 100644 --- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java +++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java @@ -82,7 +82,7 @@ public class PreloadIconDrawable extends FastBitmapDrawable { // Duration = COMPLETE_ANIM_FRACTION * DURATION_SCALE private static final float COMPLETE_ANIM_FRACTION = 1f; - private static final float SMALL_SCALE = ENABLE_DOWNLOAD_APP_UX_V2.get() ? 0.867f : 0.7f; + private static final float SMALL_SCALE = 0.7f; private static final float PROGRESS_STROKE_SCALE = ENABLE_DOWNLOAD_APP_UX_V2.get() ? 0.0655f : 0.075f; From effe64381be95a88cab0f5a2097e30ee8ac9157d Mon Sep 17 00:00:00 2001 From: Pat Manning Date: Wed, 18 Jan 2023 13:20:54 +0000 Subject: [PATCH 055/469] Reset splash icon when task icon is null to avoid caching incorrect drawable. Fix: 263365662 Test: manual Change-Id: I79390381f4cab79b9729bd819630eeb9c8b47d1d --- .../src/com/android/quickstep/views/TaskThumbnailView.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java index f6e172aad0..c71a74e3e6 100644 --- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java +++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java @@ -377,6 +377,8 @@ public class TaskThumbnailView extends View { private void updateSplashView(Drawable icon) { if (icon == null || icon.getConstantState() == null) { + mSplashViewDrawable = null; + mSplashView = null; return; } mSplashViewDrawable = icon.getConstantState().newDrawable().mutate(); From a8037620bdc21436ebbd86bd06e7fbec7b8e8d32 Mon Sep 17 00:00:00 2001 From: Nick Chameyev Date: Fri, 13 Jan 2023 16:28:24 +0000 Subject: [PATCH 056/469] Calculate StatusBar height locally in Launcher When reading the status bar inset on devices with multiple displays after switching the displays the inset is not updated immediately (SystemUI should update the height of the window first by reacting to the display switch in it's own process). Due to this race condition Launcher is very likely to calculate DeviceProfile with incorrect inset of the previous display first and update it to the correct one only after SystemUI finished the window update. To avoid this adding calculation of the StatusBar height to Launcher locally using the same utility method that is used by SystemUI. Bug: 264656380 Test: manual test that the statusbar height is calculated correctly in Launcher on a device with two displays, both on the inner and outer display and different rotations Change-Id: Ia16ecadd8fcac29082d27ca693ab7740ce6161c4 --- .../util/SystemWindowManagerProxy.java | 8 ++++++++ .../util/window/WindowManagerProxy.java | 18 ++++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java index 5dc461363b..a34888f684 100644 --- a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java +++ b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java @@ -23,6 +23,7 @@ import android.view.Surface; import android.view.WindowManager; import android.view.WindowMetrics; +import com.android.internal.policy.SystemBarUtils; import com.android.launcher3.util.WindowBounds; import com.android.launcher3.util.window.CachedDisplayInfo; import com.android.launcher3.util.window.WindowManagerProxy; @@ -44,6 +45,13 @@ public class SystemWindowManagerProxy extends WindowManagerProxy { .getRotation(); } + @Override + protected int getStatusBarHeight(Context context, boolean isPortrait, int statusBarInset) { + // See b/264656380, calculate the status bar height manually as the inset in the system + // server might not be updated by this point yet causing extra DeviceProfile updates + return SystemBarUtils.getStatusBarHeight(context); + } + @Override public ArrayMap estimateInternalDisplayBounds( Context displayInfoContext) { diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java index fb2ae732b7..4093bc913d 100644 --- a/src/com/android/launcher3/util/window/WindowManagerProxy.java +++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java @@ -161,12 +161,10 @@ public class WindowManagerProxy implements ResourceBasedOverride { insetsBuilder.setInsetsIgnoringVisibility(WindowInsets.Type.navigationBars(), newNavInsets); Insets statusBarInsets = oldInsets.getInsets(WindowInsets.Type.statusBars()); - int statusBarHeight = getDimenByName(systemRes, - (isPortrait) ? STATUS_BAR_HEIGHT_PORTRAIT : STATUS_BAR_HEIGHT_LANDSCAPE, - STATUS_BAR_HEIGHT); + Insets newStatusBarInsets = Insets.of( statusBarInsets.left, - Math.max(statusBarInsets.top, statusBarHeight), + getStatusBarHeight(context, isPortrait, statusBarInsets.top), statusBarInsets.right, statusBarInsets.bottom); insetsBuilder.setInsets(WindowInsets.Type.statusBars(), newStatusBarInsets); @@ -190,6 +188,15 @@ public class WindowManagerProxy implements ResourceBasedOverride { return result; } + protected int getStatusBarHeight(Context context, boolean isPortrait, int statusBarInset) { + Resources systemRes = context.getResources(); + int statusBarHeight = getDimenByName(systemRes, + isPortrait ? STATUS_BAR_HEIGHT_PORTRAIT : STATUS_BAR_HEIGHT_LANDSCAPE, + STATUS_BAR_HEIGHT); + + return Math.max(statusBarInset, statusBarHeight); + } + /** * Returns a list of possible WindowBounds for the display keyed on the 4 surface rotations */ @@ -212,6 +219,9 @@ public class WindowManagerProxy implements ResourceBasedOverride { boolean isTabletOrGesture = isTablet || (Utilities.ATLEAST_R && isGestureNav(context)); + // Use the status bar height resources because current system API to get the status bar + // height doesn't allow to do this for an arbitrary display, it returns value only + // for the current active display (see com.android.internal.policy.StatusBarUtils) int statusBarHeightPortrait = getDimenByName(systemRes, STATUS_BAR_HEIGHT_PORTRAIT, STATUS_BAR_HEIGHT); int statusBarHeightLandscape = getDimenByName(systemRes, From 8b85f83785f2cf7d17b6a87812e35716a88fdd8e Mon Sep 17 00:00:00 2001 From: Stefan Andonian Date: Wed, 18 Jan 2023 18:15:30 +0000 Subject: [PATCH 057/469] Add null check for NPE in FolderIcon. Bug: 265298513 Test: N/A Change-Id: Id55bd488c40edcca376df2843b1a1999aeb944be --- src/com/android/launcher3/folder/FolderIcon.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 60442f452b..e69f7811e7 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -41,6 +41,7 @@ import android.view.ViewGroup; import android.widget.FrameLayout; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.launcher3.Alarm; import com.android.launcher3.BubbleTextView; @@ -181,8 +182,11 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel return icon; } - public static FolderIcon inflateIcon(int resId, ActivityContext activity, ViewGroup group, - FolderInfo folderInfo) { + /** + * Builds a FolderIcon to be added to the Launcher + */ + public static FolderIcon inflateIcon(int resId, ActivityContext activity, + @Nullable ViewGroup group, FolderInfo folderInfo) { @SuppressWarnings("all") // suppress dead code warning final boolean error = INITIAL_ITEM_ANIMATION_DURATION >= DROP_IN_ANIMATION_DURATION; if (error) { @@ -192,8 +196,10 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel } DeviceProfile grid = activity.getDeviceProfile(); - FolderIcon icon = (FolderIcon) LayoutInflater.from(group.getContext()) - .inflate(resId, group, false); + LayoutInflater inflater = (group != null) + ? LayoutInflater.from(group.getContext()) + : activity.getLayoutInflater(); + FolderIcon icon = (FolderIcon) inflater.inflate(resId, group, false); icon.setClipToPadding(false); icon.mFolderName = icon.findViewById(R.id.folder_icon_name); From b1c47227b16855a16ab2658d4f44a2ae87b9b65f Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Tue, 17 Jan 2023 14:48:36 -0800 Subject: [PATCH 058/469] Add unit tests for TaskbarKeyguardController * Remove setting back button tint for SUW as that is now done in ag/20953768 Bug: 230027385 Change-Id: I2c157ed67487cab9ba721fa659973351780509bb --- .../taskbar/NavbarButtonsViewController.java | 8 - .../launcher3/taskbar/TaskbarControllers.java | 7 + .../launcher3/taskbar/TaskbarBaseTestCase.kt | 100 ++++++++++++ .../taskbar/TaskbarKeyguardControllerTest.kt | 150 ++++++++++++++++++ 4 files changed, 257 insertions(+), 8 deletions(-) create mode 100644 quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt create mode 100644 quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java index cbee58bc2f..2b976df802 100644 --- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java @@ -51,7 +51,6 @@ import android.annotation.IdRes; import android.annotation.LayoutRes; import android.content.pm.ActivityInfo.Config; import android.content.res.ColorStateList; -import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Color; import android.graphics.Point; @@ -725,13 +724,6 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT mPropertyHolders.add(new StatePropertyHolder( mBackButtonAlpha.get(ALPHA_INDEX_SUW), flags -> (flags & FLAG_IME_VISIBLE) == 0)); - - // TODO(b/210906568) Dark intensity is currently not propagated during setup, so set - // it based on dark theme for now. - int mode = res.getConfiguration().uiMode - & Configuration.UI_MODE_NIGHT_MASK; - boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES; - mTaskbarNavButtonDarkIntensity.updateValue(isDarkTheme ? 0 : 1); } else if (isInKidsMode) { int iconSize = res.getDimensionPixelSize( R.dimen.taskbar_icon_size_kids); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java index bc41c2b520..260876b1e9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java @@ -73,6 +73,13 @@ public class TaskbarControllers { // Roundness property for round corner above taskbar . private final AnimatedFloat mCornerRoundness = new AnimatedFloat(this::updateCornerRoundness); + /** + * Want to add a new controller? Don't forget to: + * * Call init + * * Call onDestroy + * * Add to mControllersToLog + * * Add tests by adding this controller to TaskbarBaseTestCase.kt and extending that class + */ public TaskbarControllers(TaskbarActivityContext taskbarActivityContext, TaskbarDragController taskbarDragController, TaskbarNavButtonController navButtonController, diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt new file mode 100644 index 0000000000..e8a575eb1d --- /dev/null +++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.taskbar + +import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController +import com.android.launcher3.taskbar.overlay.TaskbarOverlayController +import com.android.systemui.shared.rotation.RotationButtonController +import org.junit.Before +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +/** + * Helper class to extend to get access to all controllers. + * Gotta be careful of your relationship with this class though, it can be quite... controlling. + */ +abstract class TaskbarBaseTestCase { + + @Mock + lateinit var taskbarActivityContext: TaskbarActivityContext + @Mock + lateinit var taskbarDragController: TaskbarDragController + @Mock + lateinit var navButtonController: TaskbarNavButtonController + @Mock + lateinit var navbarButtonsViewController: NavbarButtonsViewController + @Mock + lateinit var rotationButtonController: RotationButtonController + @Mock + lateinit var taskbarDragLayerController: TaskbarDragLayerController + @Mock + lateinit var taskbarScrimViewController: TaskbarScrimViewController + @Mock + lateinit var taskbarViewController: TaskbarViewController + @Mock + lateinit var taskbarUnfoldAnimationController: TaskbarUnfoldAnimationController + @Mock + lateinit var taskbarKeyguardController: TaskbarKeyguardController + @Mock + lateinit var stashedHandleViewController: StashedHandleViewController + @Mock + lateinit var taskbarStashController: TaskbarStashController + @Mock + lateinit var taskbarEduController: TaskbarEduController + @Mock + lateinit var taskbarAutohideSuspendController: TaskbarAutohideSuspendController + @Mock + lateinit var taskbarPopupController: TaskbarPopupController + @Mock + lateinit var taskbarForceVisibleImmersiveController: TaskbarForceVisibleImmersiveController + @Mock + lateinit var taskbarAllAppsController: TaskbarAllAppsController + @Mock + lateinit var taskbarInsetsController: TaskbarInsetsController + @Mock + lateinit var voiceInteractionWindowController: VoiceInteractionWindowController + @Mock + lateinit var taskbarRecentAppsController: TaskbarRecentAppsController + @Mock + lateinit var taskbarTranslationController: TaskbarTranslationController + @Mock + lateinit var taskbarOverlayController: TaskbarOverlayController + + lateinit var mTaskbarControllers: TaskbarControllers + + @Before + open fun setup() { + /* + * NOTE: Mocking of controllers that are written in Kotlin won't work since their methods + * are final by default (and should not be changed only for tests), meaning unmockable. + * Womp, womp woooommmmppp. + * If you want to mock one of those methods, you need to make a parent interface that + * includes that method to allow mocking it. + */ + MockitoAnnotations.initMocks(this) + mTaskbarControllers = TaskbarControllers( + taskbarActivityContext, taskbarDragController, navButtonController, + navbarButtonsViewController, rotationButtonController, taskbarDragLayerController, + taskbarViewController, taskbarScrimViewController, taskbarUnfoldAnimationController, + taskbarKeyguardController, stashedHandleViewController, taskbarStashController, + taskbarEduController, taskbarAutohideSuspendController, taskbarPopupController, + taskbarForceVisibleImmersiveController, taskbarOverlayController, + taskbarAllAppsController, taskbarInsetsController, + voiceInteractionWindowController, taskbarTranslationController, + taskbarRecentAppsController + ) + } +} \ No newline at end of file diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt new file mode 100644 index 0000000000..59db8c8709 --- /dev/null +++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarKeyguardControllerTest.kt @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.taskbar + +import android.app.KeyguardManager +import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED +import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING +import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING +import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED +import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED +import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING +import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito.anyBoolean +import org.mockito.Mockito.never +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever + +class TaskbarKeyguardControllerTest : TaskbarBaseTestCase() { + + @Mock + lateinit var baseDragLayer: TaskbarDragLayer + @Mock + lateinit var keyguardManager: KeyguardManager + + @Before + override fun setup() { + super.setup() + whenever(taskbarActivityContext.getSystemService(KeyguardManager::class.java)) + .thenReturn(keyguardManager) + whenever(baseDragLayer.childCount).thenReturn(0) + whenever(taskbarActivityContext.dragLayer).thenReturn(baseDragLayer) + + taskbarKeyguardController = TaskbarKeyguardController(taskbarActivityContext) + taskbarKeyguardController.init(navbarButtonsViewController) + } + + @Test + fun uninterestingFlags_noActions() { + setFlags(0) + verify(navbarButtonsViewController, never()).setKeyguardVisible(anyBoolean(), anyBoolean()) + } + + @Test + fun keyguardShowing() { + setFlags(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING) + verify(navbarButtonsViewController, times(1)).setKeyguardVisible( + true /*isKeyguardVisible*/, + false /*isKeyguardOccluded*/) + } + + @Test + fun dozingShowing() { + setFlags(SYSUI_STATE_DEVICE_DOZING) + verify(navbarButtonsViewController, times(1)).setKeyguardVisible( + true /*isKeyguardVisible*/, + false /*isKeyguardOccluded*/) + } + + @Test + fun keyguardOccluded() { + setFlags(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) + verify(navbarButtonsViewController, times(1)).setKeyguardVisible( + false /*isKeyguardVisible*/, + true /*isKeyguardOccluded*/) + } + + @Test + fun keyguardOccludedAndDozing() { + setFlags(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED.or(SYSUI_STATE_DEVICE_DOZING)) + verify(navbarButtonsViewController, times(1)).setKeyguardVisible( + true /*isKeyguardVisible*/, + true /*isKeyguardOccluded*/) + } + + @Test + fun deviceInsecure_hideBackForBouncer() { + whenever(keyguardManager.isDeviceSecure).thenReturn(false) + setFlags(SYSUI_STATE_OVERVIEW_DISABLED + .or(SYSUI_STATE_HOME_DISABLED) + .or(SYSUI_STATE_BOUNCER_SHOWING)) + + verify(navbarButtonsViewController, times(1)).setBackForBouncer(false) + } + + @Test + fun deviceSecure_showBackForBouncer() { + whenever(keyguardManager.isDeviceSecure).thenReturn(true) + setFlags(SYSUI_STATE_OVERVIEW_DISABLED + .or(SYSUI_STATE_HOME_DISABLED) + .or(SYSUI_STATE_BOUNCER_SHOWING)) + + verify(navbarButtonsViewController, times(1)).setBackForBouncer(true) + } + + @Test + fun homeShowing_hideBackForBouncer() { + whenever(keyguardManager.isDeviceSecure).thenReturn(true) + setFlags(SYSUI_STATE_OVERVIEW_DISABLED + .or(SYSUI_STATE_BOUNCER_SHOWING)) + + verify(navbarButtonsViewController, times(1)).setBackForBouncer(false) + } + + @Test + fun overviewShowing_hideBackForBouncer() { + whenever(keyguardManager.isDeviceSecure).thenReturn(true) + setFlags(SYSUI_STATE_HOME_DISABLED + .or(SYSUI_STATE_BOUNCER_SHOWING)) + + verify(navbarButtonsViewController, times(1)).setBackForBouncer(false) + } + + @Test + fun backDisabled_hideBackForBouncer() { + whenever(keyguardManager.isDeviceSecure).thenReturn(true) + setFlags(SYSUI_STATE_BACK_DISABLED + .or(SYSUI_STATE_BOUNCER_SHOWING)) + + verify(navbarButtonsViewController, times(1)).setBackForBouncer(false) + + // back disabled along with home and overview + setFlags(SYSUI_STATE_BACK_DISABLED + .or(SYSUI_STATE_HOME_DISABLED) + .or(SYSUI_STATE_OVERVIEW_DISABLED) + .or(SYSUI_STATE_BOUNCER_SHOWING)) + + verify(navbarButtonsViewController, times(2)).setBackForBouncer(false) + } + + private fun setFlags(flags: Int) { + taskbarKeyguardController.updateStateForSysuiFlags(flags) + } +} \ No newline at end of file From 7450874e28b4ff617f3b1a9c5f25bb356719c44d Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 18 Jan 2023 15:34:19 -0800 Subject: [PATCH 059/469] Fixing while navication bar in all-apps in 3-button mode. Launcher was not accounting for all-apps-nav-bar-scrim when computing sysui-flags Bug: 194486821 Test: Verified on device Change-Id: I692977fba5999bbab9737bac4b27c15c2912e6af --- .../allapps/AllAppsTransitionController.java | 18 +++++++++++++++++- .../allapps/BaseAllAppsContainerView.java | 11 +++++++++-- .../allapps/LauncherAllAppsContainerView.java | 2 +- .../launcher3/util/SystemUiController.java | 3 ++- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 6f6f86b105..f7b0d966f9 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -25,6 +25,9 @@ import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER; import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS; +import static com.android.launcher3.util.SystemUiController.FLAG_DARK_NAV; +import static com.android.launcher3.util.SystemUiController.FLAG_LIGHT_NAV; +import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS; import android.animation.Animator; import android.animation.Animator.AnimatorListener; @@ -54,6 +57,7 @@ import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.util.MultiPropertyFactory; import com.android.launcher3.util.MultiPropertyFactory.MultiProperty; import com.android.launcher3.util.MultiValueAlpha; +import com.android.launcher3.util.Themes; import com.android.launcher3.views.ScrimView; /** @@ -73,6 +77,8 @@ public class AllAppsTransitionController public static final float SWIPE_ALL_APPS_TO_HOME_MIN_SCALE = 0.9f; private static final int REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS = 200; + private static final float NAV_BAR_COLOR_FORCE_UPDATE_THRESHOLD = 0.1f; + public static final FloatProperty ALL_APPS_PROGRESS = new FloatProperty("allAppsProgress") { @@ -151,6 +157,8 @@ public class AllAppsTransitionController private final Launcher mLauncher; private final AnimatedFloat mAllAppScale = new AnimatedFloat(this::onScaleProgressChanged); + private final int mNavScrimFlag; + private boolean mIsVerticalLayout; // Whether this class should take care of closing the keyboard. @@ -177,10 +185,13 @@ public class AllAppsTransitionController public AllAppsTransitionController(Launcher l) { mLauncher = l; DeviceProfile dp = mLauncher.getDeviceProfile(); - setShiftRange(dp.allAppsShiftRange); mProgress = 1f; mIsVerticalLayout = dp.isVerticalBarLayout(); mIsTablet = dp.isTablet; + mNavScrimFlag = Themes.getAttrBoolean(l, R.attr.isMainColorDark) + ? FLAG_DARK_NAV : FLAG_LIGHT_NAV; + + setShiftRange(dp.allAppsShiftRange); mLauncher.addOnDeviceProfileChangeListener(this); } @@ -213,6 +224,11 @@ public class AllAppsTransitionController mProgress = progress; getAppsViewProgressTranslationY().setValue(mProgress * mShiftRange); mLauncher.onAllAppsTransition(1 - progress); + + boolean hasScrim = progress < NAV_BAR_COLOR_FORCE_UPDATE_THRESHOLD + && mLauncher.getAppsView().getNavBarScrimHeight() > 0; + mLauncher.getSystemUiController().updateUiState( + UI_STATE_ALL_APPS, hasScrim ? mNavScrimFlag : 0); } public float getProgress() { diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java index 1c676915fd..9bb825084b 100644 --- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java @@ -520,13 +520,20 @@ public abstract class BaseAllAppsContainerView Date: Wed, 18 Jan 2023 17:20:03 +0000 Subject: [PATCH 060/469] Add OPTIMIZE_MEASURE flag to QuickSteplauncher window This flag allows to avoid an initial measure pass by getting the window size by the LayoutParams. Test: Recorded a trace before and after this change and compared the doFrames Bug: 265150323 Change-Id: I2c1e440b73437df9f328d64c98d160bed11282f3 --- .../com/android/launcher3/uioverrides/QuickstepLauncher.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 0114457909..078865fd02 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -16,6 +16,7 @@ package com.android.launcher3.uioverrides; import static android.os.Trace.TRACE_TAG_APP; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE; import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT; @@ -543,6 +544,7 @@ public class QuickstepLauncher extends Launcher { if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) { mViewCapture = ViewCapture.getInstance().startCapture(getWindow()); } + getWindow().addPrivateFlags(PRIVATE_FLAG_OPTIMIZE_MEASURE); } @Override From ffe8cc01d5c07ad458032de3c51a036bfc85f1a8 Mon Sep 17 00:00:00 2001 From: Jagrut Desai Date: Thu, 12 Jan 2023 10:20:17 -0800 Subject: [PATCH 061/469] Adding a debug feature flag for taskbar pinning. Bug: 265307333 Test: cl Change-Id: I95cea392924fb9dc2acff99cd97a5fb4ddc32fbc --- src/com/android/launcher3/config/FeatureFlags.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index daf83d4b17..5f9cfe1391 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -385,6 +385,11 @@ public final class FeatureFlags { "ENABLE_MULTI_INSTANCE", false, "Enables creation and filtering of multiple task instances in overview"); + public static final BooleanFlag ENABLE_TASKBAR_PINNING = getDebugFlag( + "ENABLE_TASKBAR_PINNING", false, + "Enables taskbar pinning to allow user to switch between transient and persistent " + + "taskbar flavors"); + public static void initialize(Context context) { synchronized (sDebugFlags) { for (DebugFlag flag : sDebugFlags) { From f6d75c98a94d173ae59afc9bd126d1e72d1b28fa Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 19 Jan 2023 21:59:07 +0000 Subject: [PATCH 062/469] Prevent falling into OtherActivityInputConsumer when over lockscreen - When opening the emergency dialer from the bouncer, there's a brief period where notificationPanelExpanded=false and occluded=false which currently means canStartSystemGesture=true and isKeyguardShowingOccluded=false, which falls through to other activity input consumer. In this state, we can't properly finish the gesture stream because Launcher never shows (it's behind the lockscreen) and the recents animation never finishes. Bug: 242704576 Test: atest NexusLauncherTests Change-Id: I7795abe0aebefca2d42998a672abfd12f951d501 --- .../src/com/android/quickstep/RecentsAnimationDeviceState.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index 99a02e15ce..8d17cf4b81 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -41,6 +41,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_O import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED; import android.app.ActivityManager; @@ -419,6 +420,7 @@ public class RecentsAnimationDeviceState implements || mRotationTouchHelper.isTaskListFrozen(); return canStartWithNavHidden && (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0 + && (mSystemUiStateFlags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING) == 0 && (mSystemUiStateFlags & SYSUI_STATE_QUICK_SETTINGS_EXPANDED) == 0 && (mSystemUiStateFlags & SYSUI_STATE_MAGNIFICATION_OVERLAP) == 0 && ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0 From 0df9f8a5c406a14d29cb48157683d7b925e8a2c3 Mon Sep 17 00:00:00 2001 From: Holly Sun Date: Thu, 19 Jan 2023 14:55:36 -0800 Subject: [PATCH 063/469] [QL-v3] Add feature flag. Bug: 261629630 Test: manual Change-Id: Ibf499fba97ef2e5f56a61cb254180bb41841f003 --- src/com/android/launcher3/config/FeatureFlags.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 082f6a1a95..7148a19619 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -304,6 +304,9 @@ public final class FeatureFlags { "FOLDABLE_WORKSPACE_REORDER", true, "In foldables, when reordering the icons and widgets, is now going to use both sides"); + public static final BooleanFlag ENABLE_QUICK_LAUNCH_V3 = new DeviceFlag( + "ENABLE_QUICK_LAUNCH_V3", false, "Quick Launch V3"); + public static final BooleanFlag ENABLE_WIDGET_PICKER_DEPTH = new DeviceFlag( "ENABLE_WIDGET_PICKER_DEPTH", true, "Enable changing depth in widget picker."); From beecffed7caebe0186f852826acaee2c717b0f29 Mon Sep 17 00:00:00 2001 From: Nicolo' Mazzucato Date: Fri, 20 Jan 2023 12:28:33 +0000 Subject: [PATCH 064/469] Fix CUJ_RECENTS_SCROLLING The atom was output also when swiping from the bottom to open overview. Now, it's only reported on horizontal swipes between tasks. Fixes: 265790869 Test: checked perfetto traces Change-Id: Ic70f440a30ea2c61e44f84098d84b0485a85905e --- quickstep/src/com/android/quickstep/views/RecentsView.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index c11f7ed27a..2726cc3817 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -1351,8 +1351,10 @@ public abstract class RecentsView Date: Fri, 20 Jan 2023 20:51:16 +0000 Subject: [PATCH 065/469] Destroy taskbar if it's not present in DeviceProfile Bug: 254119092 Test: manual Change-Id: I9eb95cc90c92bf93d9220b05406d4589b2cb196f --- .../com/android/launcher3/taskbar/TaskbarManager.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java index 98c45d5f43..a58906fe80 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java @@ -156,10 +156,14 @@ public class TaskbarManager { } else { // Config change might be handled without re-creating the taskbar if (mTaskbarActivityContext != null) { - if (dp != null && isTaskbarPresent(dp)) { - mTaskbarActivityContext.updateDeviceProfile(dp, mNavMode); + if (dp != null && !isTaskbarPresent(dp)) { + destroyExistingTaskbar(); + } else { + if (dp != null && isTaskbarPresent(dp)) { + mTaskbarActivityContext.updateDeviceProfile(dp, mNavMode); + } + mTaskbarActivityContext.onConfigurationChanged(configDiff); } - mTaskbarActivityContext.onConfigurationChanged(configDiff); } } mOldConfig = newConfig; From 9db95e89b21713b2ae798ef340b80bd5885c4dca Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Tue, 17 Jan 2023 09:41:10 -0800 Subject: [PATCH 066/469] Support desktop tasks in recents animation Desktop tasks are using freeform windowing mode. Update recents animation to support freeform tasks when desktop mode feature flag is enabled. Changes: - set initial size for freeform tasks to be the size of the thumbnail instead of size of the screen - support multiple individual remote animation targets when starting the recents animation TODO: - there are flickers when starting and ending the recents animation Bug: 263264985 Test: swipe up when more than 1 desktop task is visible Change-Id: I27ee02774281b3a433d779c0bb8825cdb6ea5457 --- .../android/quickstep/AbsSwipeUpHandler.java | 7 +- .../quickstep/RecentsAnimationTargets.java | 22 +++++++ .../android/quickstep/RemoteTargetGluer.java | 66 +++++++++++++++++-- .../com/android/quickstep/SystemUiProxy.java | 12 ++++ .../com/android/quickstep/TaskViewUtils.java | 9 ++- .../quickstep/util/TaskViewSimulator.java | 15 +++++ .../android/quickstep/views/RecentsView.java | 13 +++- 7 files changed, 134 insertions(+), 10 deletions(-) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 45f6742bb8..73fbcd6dad 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -127,6 +127,7 @@ import com.android.quickstep.util.SurfaceTransaction; import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.SwipePipToHomeAnimator; import com.android.quickstep.util.TaskViewSimulator; +import com.android.quickstep.views.DesktopTaskView; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.Task; @@ -877,7 +878,11 @@ public abstract class AbsSwipeUpHandler, public void onRecentsAnimationStart(RecentsAnimationController controller, RecentsAnimationTargets targets) { super.onRecentsAnimationStart(controller, targets); - mRemoteTargetHandles = mTargetGluer.assignTargetsForSplitScreen(mContext, targets); + if (DesktopTaskView.DESKTOP_MODE_SUPPORTED && targets.hasDesktopTasks()) { + mRemoteTargetHandles = mTargetGluer.assignTargetsForDesktop(targets); + } else { + mRemoteTargetHandles = mTargetGluer.assignTargetsForSplitScreen(mContext, targets); + } mRecentsAnimationController = controller; mRecentsAnimationTargets = targets; mSwipePipToHomeReleaseCheck = new RemoteAnimationTargets.ReleaseCheck(); diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java index 388e1256d8..15e1365998 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java @@ -15,11 +15,15 @@ */ package com.android.quickstep; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.view.RemoteAnimationTarget.MODE_CLOSING; +import android.app.WindowConfiguration; import android.graphics.Rect; import android.view.RemoteAnimationTarget; +import com.android.quickstep.views.DesktopTaskView; + /** * Extension of {@link RemoteAnimationTargets} with additional information about swipe * up animation @@ -40,4 +44,22 @@ public class RecentsAnimationTargets extends RemoteAnimationTargets { public boolean hasTargets() { return unfilteredApps.length != 0; } + + /** + * Check if target apps contain desktop tasks which have windowing mode set to {@link + * WindowConfiguration#WINDOWING_MODE_FREEFORM} + * + * @return {@code true} if at least one target app is a desktop task + */ + public boolean hasDesktopTasks() { + if (!DesktopTaskView.DESKTOP_MODE_SUPPORTED) { + return false; + } + for (RemoteAnimationTarget target : apps) { + if (target.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FREEFORM) { + return true; + } + } + return false; + } } diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java index 4c41bef26a..9b00dcf765 100644 --- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java +++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java @@ -26,6 +26,7 @@ import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds; import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.util.TransformParams; +import com.android.quickstep.views.DesktopTaskView; import java.util.ArrayList; @@ -41,8 +42,8 @@ public class RemoteTargetGluer { * Use this constructor if remote targets are split-screen independent */ public RemoteTargetGluer(Context context, BaseActivityInterface sizingStrategy, - RemoteAnimationTargets targets) { - mRemoteTargetHandles = createHandles(context, sizingStrategy, targets.apps.length); + RemoteAnimationTargets targets, boolean forDesktop) { + init(context, sizingStrategy, targets.apps.length, forDesktop); } /** @@ -50,15 +51,31 @@ public class RemoteTargetGluer { * running tasks */ public RemoteTargetGluer(Context context, BaseActivityInterface sizingStrategy) { + if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) { + // TODO: binder call, only for prototyping. Creating the gluer should be postponed so + // we can create it when we have the remote animation targets ready. + int desktopTasks = SystemUiProxy.INSTANCE.get(context).getVisibleDesktopTaskCount(); + if (desktopTasks > 0) { + init(context, sizingStrategy, desktopTasks, true /* forDesktop */); + return; + } + } + int[] splitIds = TopTaskTracker.INSTANCE.get(context).getRunningSplitTaskIds(); - mRemoteTargetHandles = createHandles(context, sizingStrategy, splitIds.length == 2 ? 2 : 1); + init(context, sizingStrategy, splitIds.length == 2 ? 2 : 1, false /* forDesktop */); + } + + private void init(Context context, BaseActivityInterface sizingStrategy, int numHandles, + boolean forDesktop) { + mRemoteTargetHandles = createHandles(context, sizingStrategy, numHandles, forDesktop); } private RemoteTargetHandle[] createHandles(Context context, - BaseActivityInterface sizingStrategy, int numHandles) { + BaseActivityInterface sizingStrategy, int numHandles, boolean forDesktop) { RemoteTargetHandle[] handles = new RemoteTargetHandle[numHandles]; for (int i = 0; i < numHandles; i++) { TaskViewSimulator tvs = new TaskViewSimulator(context, sizingStrategy); + tvs.setIsDesktopTask(forDesktop); TransformParams transformParams = new TransformParams(); handles[i] = new RemoteTargetHandle(tvs, transformParams); } @@ -135,6 +152,20 @@ public class RemoteTargetGluer { return mRemoteTargetHandles; } + /** + * Similar to {@link #assignTargets(RemoteAnimationTargets)}, except this creates distinct + * transform params per app in {@code targets.apps} list. + */ + public RemoteTargetHandle[] assignTargetsForDesktop(RemoteAnimationTargets targets) { + for (int i = 0; i < mRemoteTargetHandles.length; i++) { + RemoteAnimationTarget primaryTaskTarget = targets.apps[i]; + mRemoteTargetHandles[i].mTransformParams.setTargetSet( + createRemoteAnimationTargetsForTaskId(targets, primaryTaskTarget.taskId)); + mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(primaryTaskTarget, null); + } + return mRemoteTargetHandles; + } + private Rect getStartBounds(RemoteAnimationTarget target) { return target.startBounds == null ? target.screenSpaceBounds : target.startBounds; } @@ -172,6 +203,33 @@ public class RemoteTargetGluer { filteredApps, targets.wallpapers, targets.nonApps, targets.targetMode); } + /** + * Ensures that we only animate one specific app target. Includes ancillary targets such as + * home/recents + * + * @param targets remote animation targets to filter + * @param taskId id for a task that we want this remote animation to apply to + * @return {@link RemoteAnimationTargets} where app target only includes the app that has the + * {@code taskId} that was passed in + */ + private RemoteAnimationTargets createRemoteAnimationTargetsForTaskId( + RemoteAnimationTargets targets, int taskId) { + RemoteAnimationTarget[] targetApp = null; + for (RemoteAnimationTarget targetCompat : targets.unfilteredApps) { + if (targetCompat.taskId == taskId) { + targetApp = new RemoteAnimationTarget[]{targetCompat}; + break; + } + } + + if (targetApp == null) { + targetApp = new RemoteAnimationTarget[0]; + } + + return new RemoteAnimationTargets(targetApp, targets.wallpapers, targets.nonApps, + targets.targetMode); + } + public RemoteTargetHandle[] getRemoteTargetHandles() { return mRemoteTargetHandles; } diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index bb973342d5..abca46a985 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -952,4 +952,16 @@ public class SystemUiProxy implements ISystemUiProxy { } } } + + /** Call shell to get number of visible freeform tasks */ + public int getVisibleDesktopTaskCount() { + if (mDesktopMode != null) { + try { + return mDesktopMode.getVisibleTaskCount(); + } catch (RemoteException e) { + Log.w(TAG, "Failed call getVisibleDesktopTaskCount", e); + } + } + return 0; + } } diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java index 67de4b1679..1a72e3f746 100644 --- a/quickstep/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java @@ -39,6 +39,7 @@ import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR; import static com.android.launcher3.anim.Interpolators.clampToProgress; import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE; +import static com.android.quickstep.views.DesktopTaskView.DESKTOP_MODE_SUPPORTED; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -78,6 +79,7 @@ import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties; import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.util.TransformParams; +import com.android.quickstep.views.DesktopTaskView; import com.android.quickstep.views.GroupedTaskView; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskThumbnailView; @@ -182,9 +184,12 @@ public final class TaskViewUtils { // Re-use existing handles remoteTargetHandles = recentsViewHandles; } else { + boolean forDesktop = DESKTOP_MODE_SUPPORTED && v instanceof DesktopTaskView; RemoteTargetGluer gluer = new RemoteTargetGluer(v.getContext(), - recentsView.getSizeStrategy(), targets); - if (v.containsMultipleTasks()) { + recentsView.getSizeStrategy(), targets, forDesktop); + if (forDesktop) { + remoteTargetHandles = gluer.assignTargetsForDesktop(targets); + } else if (v.containsMultipleTasks()) { remoteTargetHandles = gluer.assignTargetsForSplitScreen(targets, v.getTaskIds()); } else { remoteTargetHandles = gluer.assignTargets(targets); diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java index 04af19f729..69f9ce3447 100644 --- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java +++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java @@ -104,6 +104,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { private SplitBounds mSplitBounds; private Boolean mDrawsBelowRecents = null; private boolean mIsGridTask; + private boolean mIsDesktopTask; private int mTaskRectTranslationX; private int mTaskRectTranslationY; @@ -145,6 +146,13 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { if (mDp == null) { return 1; } + + if (mIsDesktopTask) { + mTaskRect.set(mThumbnailPosition); + mPivot.set(mTaskRect.centerX(), mTaskRect.centerY()); + return 1; + } + if (mIsGridTask) { mSizeStrategy.calculateGridTaskSize(mContext, mDp, mTaskRect, mOrientationState.getOrientationHandler()); @@ -227,6 +235,13 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { mIsGridTask = isGridTask; } + /** + * Sets whether this task is part of desktop tasks in overview. + */ + public void setIsDesktopTask(boolean desktop) { + mIsDesktopTask = desktop; + } + /** * Apply translations on TaskRect's starting location. */ diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index c11f7ed27a..f3e2e7963e 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -4909,9 +4909,16 @@ public abstract class RecentsView Date: Wed, 21 Dec 2022 03:23:32 +0000 Subject: [PATCH 067/469] Implement initial transient Taskbar EDU tooltips. Since this tooltip looks and behaves differently than the existing EDU sheet, it has its own view and controller implementations (I also may have wanted to write some Kotlin). To keep transient taskbar open while on the second EDU step, another autohide suspend flag is defined. Additionally, special casing is added to avoid hiding transient taskbar if autohiding is currently suspended. Tooltips use the same assets as the bottom sheet for now, and are scaled down to fit the tooltip dimensions. Reset `Taskbar Education` in Developer Options to try EDU again. [Demos] - First: https://screenshot.googleplex.com/ASBeGvrb2EA5wEF.png - Second: https://screenshot.googleplex.com/7fnfcTh9bMYezDc.png Test: Manual Test: Open app, see swipe-up tooltip. Test: Swipe up to show transient taskbar, see features tooltip. Bug: 263157739 Fix: 258460203 Change-Id: I473f5fccbae279db0614763b640da0a120b6b7f7 --- .../res/drawable/bg_taskbar_edu_tooltip.xml | 22 +++ quickstep/res/layout/taskbar_edu_features.xml | 87 ++++++++ quickstep/res/layout/taskbar_edu_swipe.xml | 43 ++++ quickstep/res/layout/taskbar_edu_tooltip.xml | 41 ++++ quickstep/res/values/dimens.xml | 12 ++ quickstep/res/values/strings.xml | 6 +- quickstep/res/values/styles.xml | 12 ++ .../launcher3/QuickstepTransitionManager.java | 4 +- .../launcher3/taskbar/BaseTaskbarContext.java | 9 + .../taskbar/LauncherTaskbarUIController.java | 48 ++++- .../taskbar/TaskbarActivityContext.java | 4 +- .../TaskbarAutohideSuspendController.java | 4 + .../launcher3/taskbar/TaskbarControllers.java | 9 +- .../launcher3/taskbar/TaskbarEduTooltip.kt | 187 ++++++++++++++++++ .../taskbar/TaskbarEduTooltipController.kt | 147 ++++++++++++++ .../taskbar/TaskbarStashController.java | 5 + .../taskbar/TaskbarTranslationController.java | 8 +- .../overlay/TaskbarOverlayContext.java | 9 - .../launcher3/taskbar/TaskbarBaseTestCase.kt | 107 +++++----- .../launcher3/util/OnboardingPrefs.java | 7 +- 20 files changed, 686 insertions(+), 85 deletions(-) create mode 100644 quickstep/res/drawable/bg_taskbar_edu_tooltip.xml create mode 100644 quickstep/res/layout/taskbar_edu_features.xml create mode 100644 quickstep/res/layout/taskbar_edu_swipe.xml create mode 100644 quickstep/res/layout/taskbar_edu_tooltip.xml create mode 100644 quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltip.kt create mode 100644 quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt diff --git a/quickstep/res/drawable/bg_taskbar_edu_tooltip.xml b/quickstep/res/drawable/bg_taskbar_edu_tooltip.xml new file mode 100644 index 0000000000..a20f7daf2c --- /dev/null +++ b/quickstep/res/drawable/bg_taskbar_edu_tooltip.xml @@ -0,0 +1,22 @@ + + + + + + + \ No newline at end of file diff --git a/quickstep/res/layout/taskbar_edu_features.xml b/quickstep/res/layout/taskbar_edu_features.xml new file mode 100644 index 0000000000..72517b1587 --- /dev/null +++ b/quickstep/res/layout/taskbar_edu_features.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + +