summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--quickstep/src/com/android/launcher3/statehandlers/DepthController.java90
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java26
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java5
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt4
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java2
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt29
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt39
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt31
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt25
-rw-r--r--quickstep/tests/src/com/android/launcher3/statehandlers/DepthControllerTest.kt105
-rw-r--r--quickstep/tests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt37
-rw-r--r--src/com/android/launcher3/model/LoaderCursor.java4
-rw-r--r--tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java26
13 files changed, 265 insertions, 158 deletions
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index 360210b010..d9808308e8 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -30,6 +30,8 @@ import android.view.View;
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
+import androidx.annotation.VisibleForTesting;
+
import com.android.launcher3.BaseActivity;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
@@ -46,8 +48,8 @@ import java.util.function.Consumer;
*/
public class DepthController extends BaseDepthController implements StateHandler<LauncherState>,
BaseActivity.MultiWindowModeChangedListener {
-
- private final ViewTreeObserver.OnDrawListener mOnDrawListener = this::onLauncherDraw;
+ @VisibleForTesting
+ final ViewTreeObserver.OnDrawListener mOnDrawListener = this::onLauncherDraw;
private final Consumer<Boolean> mCrossWindowBlurListener = this::setCrossWindowBlursEnabled;
@@ -58,6 +60,10 @@ public class DepthController extends BaseDepthController implements StateHandler
private View.OnAttachStateChangeListener mOnAttachListener;
+ // Ensure {@link mOnDrawListener} is added only once to avoid spamming DragLayer's mRunQueue
+ // via {@link View#post(Runnable)}
+ private boolean mIsOnDrawListenerAdded = false;
+
public DepthController(Launcher l) {
super(l);
}
@@ -66,33 +72,37 @@ public class DepthController extends BaseDepthController implements StateHandler
View view = mLauncher.getDragLayer();
ViewRootImpl viewRootImpl = view.getViewRootImpl();
setBaseSurface(viewRootImpl != null ? viewRootImpl.getSurfaceControl() : null);
- view.post(() -> view.getViewTreeObserver().removeOnDrawListener(mOnDrawListener));
+ view.post(this::removeOnDrawListener);
}
private void ensureDependencies() {
- if (mLauncher.getRootView() != null && mOnAttachListener == null) {
- View rootView = mLauncher.getRootView();
- mOnAttachListener = new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View view) {
- UI_HELPER_EXECUTOR.execute(() ->
- CrossWindowBlurListeners.getInstance().addListener(
- mLauncher.getMainExecutor(), mCrossWindowBlurListener));
- mLauncher.getScrimView().addOpaquenessListener(mOpaquenessListener);
-
- // To handle the case where window token is invalid during last setDepth call.
- applyDepthAndBlur();
- }
-
- @Override
- public void onViewDetachedFromWindow(View view) {
- removeSecondaryListeners();
- }
- };
- rootView.addOnAttachStateChangeListener(mOnAttachListener);
- if (rootView.isAttachedToWindow()) {
- mOnAttachListener.onViewAttachedToWindow(rootView);
+ View rootView = mLauncher.getRootView();
+ if (rootView == null) {
+ return;
+ }
+ if (mOnAttachListener != null) {
+ return;
+ }
+ mOnAttachListener = new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View view) {
+ UI_HELPER_EXECUTOR.execute(() ->
+ CrossWindowBlurListeners.getInstance().addListener(
+ mLauncher.getMainExecutor(), mCrossWindowBlurListener));
+ mLauncher.getScrimView().addOpaquenessListener(mOpaquenessListener);
+
+ // To handle the case where window token is invalid during last setDepth call.
+ applyDepthAndBlur();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View view) {
+ removeSecondaryListeners();
}
+ };
+ rootView.addOnAttachStateChangeListener(mOnAttachListener);
+ if (rootView.isAttachedToWindow()) {
+ mOnAttachListener.onViewAttachedToWindow(rootView);
}
}
@@ -109,11 +119,9 @@ public class DepthController extends BaseDepthController implements StateHandler
}
private void removeSecondaryListeners() {
- if (mCrossWindowBlurListener != null) {
- UI_HELPER_EXECUTOR.execute(() ->
- CrossWindowBlurListeners.getInstance()
- .removeListener(mCrossWindowBlurListener));
- }
+ UI_HELPER_EXECUTOR.execute(() ->
+ CrossWindowBlurListeners.getInstance()
+ .removeListener(mCrossWindowBlurListener));
if (mOpaquenessListener != null) {
mLauncher.getScrimView().removeOpaquenessListener(mOpaquenessListener);
}
@@ -124,9 +132,9 @@ public class DepthController extends BaseDepthController implements StateHandler
*/
public void setActivityStarted(boolean isStarted) {
if (isStarted) {
- mLauncher.getDragLayer().getViewTreeObserver().addOnDrawListener(mOnDrawListener);
+ addOnDrawListener();
} else {
- mLauncher.getDragLayer().getViewTreeObserver().removeOnDrawListener(mOnDrawListener);
+ removeOnDrawListener();
setBaseSurface(null);
}
}
@@ -139,7 +147,7 @@ public class DepthController extends BaseDepthController implements StateHandler
stateDepth.setValue(toState.getDepth(mLauncher));
if (toState == LauncherState.BACKGROUND_APP) {
- mLauncher.getDragLayer().getViewTreeObserver().addOnDrawListener(mOnDrawListener);
+ addOnDrawListener();
}
}
@@ -165,7 +173,23 @@ public class DepthController extends BaseDepthController implements StateHandler
@Override
protected void onInvalidSurface() {
// Lets wait for surface to become valid again
+ addOnDrawListener();
+ }
+
+ private void addOnDrawListener() {
+ if (mIsOnDrawListenerAdded) {
+ return;
+ }
mLauncher.getDragLayer().getViewTreeObserver().addOnDrawListener(mOnDrawListener);
+ mIsOnDrawListenerAdded = true;
+ }
+
+ private void removeOnDrawListener() {
+ if (!mIsOnDrawListenerAdded) {
+ return;
+ }
+ mLauncher.getDragLayer().getViewTreeObserver().removeOnDrawListener(mOnDrawListener);
+ mIsOnDrawListenerAdded = false;
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
index ab147bbcd9..515cfe2af0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
@@ -47,7 +47,6 @@ import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.app.animation.Interpolators;
import com.android.internal.jank.Cuj;
-import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
@@ -180,17 +179,6 @@ public class KeyboardQuickSwitchView extends ConstraintLayout {
mIsRtl = Utilities.isRtl(resources);
- if (Flags.taskbarOverflow()) {
- initializeScrollArrows();
-
- if (mIsRtl) {
- mStartScrollArrow.setContentDescription(
- resources.getString(R.string.quick_switch_scroll_arrow_right));
- mEndScrollArrow.setContentDescription(
- resources.getString(R.string.quick_switch_scroll_arrow_left));
- }
- }
-
TypefaceUtils.setTypeface(
mNoRecentItemsPane.findViewById(R.id.no_recent_items_text),
FontFamily.GSF_LABEL_LARGE);
@@ -359,9 +347,21 @@ public class KeyboardQuickSwitchView extends ConstraintLayout {
});
}
- private void initializeScrollArrows() {
+
+ void enableScrollArrowSupport() {
+ if (mSupportsScrollArrows) {
+ return;
+ }
mSupportsScrollArrows = true;
+ if (mIsRtl) {
+ mStartScrollArrow.setContentDescription(
+ getResources().getString(R.string.quick_switch_scroll_arrow_right));
+ mEndScrollArrow.setContentDescription(
+ getResources().getString(R.string.quick_switch_scroll_arrow_left));
+ }
+
+
mStartScrollArrow.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index b5f2532482..a312d5f70b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -34,6 +34,7 @@ import androidx.annotation.Nullable;
import com.android.internal.jank.Cuj;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
@@ -119,6 +120,10 @@ public class KeyboardQuickSwitchViewController {
mWasDesktopTaskFilteredOut = wasDesktopTaskFilteredOut;
mWasOpenedFromTaskbar = wasOpenedFromTaskbar;
+ if (Flags.taskbarOverflow() && wasOpenedFromTaskbar) {
+ mKeyboardQuickSwitchView.enableScrollArrowSupport();
+ }
+
mKeyboardQuickSwitchView.applyLoadPlan(
mOverlayContext,
tasks,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index f342fa52b3..cc6cc645f5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -348,13 +348,17 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas
controllers.bubbleControllers.isPresent &&
controllers.bubbleControllers.get().bubbleBarViewController.isBubbleBarVisible()
var insetsIsTouchableRegion = true
+ // Prevents the taskbar from taking touches and conflicting with setup wizard
if (
context.isPhoneButtonNavMode &&
+ context.isUserSetupComplete &&
(!controllers.navbarButtonsViewController.isImeVisible ||
!controllers.navbarButtonsViewController.isImeRenderingNavButtons)
) {
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME)
insetsIsTouchableRegion = false
+ debugTouchableRegion.lastSetTouchableReason =
+ "Phone button nav mode: Fullscreen touchable, IME not affecting nav buttons"
} else if (context.dragLayer.alpha < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) {
// Let touches pass through us.
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index f15ef8c52c..ac9d2ba8bf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -496,7 +496,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
float finalScale;
TaskbarSharedState sharedState = mControllers.getSharedState();
- if (sharedState != null || mControllers.getSharedState().startTaskbarVariantIsTransient) {
+ if (sharedState != null && sharedState.startTaskbarVariantIsTransient) {
finalScale = mapRange(scale, 1f, ((float) mPersistentIconSize / mTransientIconSize));
} else {
finalScale = mapRange(scale, ((float) mTransientIconSize / mPersistentIconSize), 1f);
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
index e487f9fd40..3712a76eab 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
@@ -27,7 +27,6 @@ import android.widget.Space
import com.android.launcher3.DeviceProfile
import com.android.launcher3.R
import com.android.launcher3.Utilities
-import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter
/**
@@ -48,7 +47,7 @@ abstract class AbstractNavButtonLayoutter(
protected val startContextualContainer: ViewGroup,
protected val imeSwitcher: ImageView?,
protected val a11yButton: ImageView?,
- protected val space: Space?
+ protected val space: Space?,
) : NavButtonLayoutter {
protected val homeButton: ImageView? = navButtonContainer.findViewById(R.id.home)
protected val recentsButton: ImageView? = navButtonContainer.findViewById(R.id.recent_apps)
@@ -69,26 +68,34 @@ abstract class AbstractNavButtonLayoutter(
val params =
FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT
+ ViewGroup.LayoutParams.MATCH_PARENT,
)
params.gravity = Gravity.CENTER
return params
}
+ /**
+ * Adjusts the layout parameters of the nav bar container for setup in phone mode.
+ *
+ * @param nearestTouchFrameLayoutParams The layout parameters of the navButtonsView, which is
+ * the ViewGroup that contains start, end, nav button ViewGroups
+ * @param deviceProfile The device profile containing information about the device's
+ * configuration.
+ */
fun adjustForSetupInPhoneMode(
- navButtonsLayoutParams: FrameLayout.LayoutParams,
- navButtonsViewLayoutParams: FrameLayout.LayoutParams,
- deviceProfile: DeviceProfile
+ nearestTouchFrameLayoutParams: FrameLayout.LayoutParams,
+ deviceProfile: DeviceProfile,
) {
val phoneOrPortraitSetupMargin =
resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_suw_margin)
- navButtonsLayoutParams.marginStart = phoneOrPortraitSetupMargin
- navButtonsLayoutParams.bottomMargin =
+ nearestTouchFrameLayoutParams.marginStart = phoneOrPortraitSetupMargin
+ nearestTouchFrameLayoutParams.bottomMargin =
if (!deviceProfile.isLandscape) 0
else
phoneOrPortraitSetupMargin -
- resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) / 2
- navButtonsViewLayoutParams.height =
+ resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) / 2
+
+ nearestTouchFrameLayoutParams.height =
resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_suw_height)
}
@@ -97,7 +104,7 @@ abstract class AbstractNavButtonLayoutter(
buttonSize: Int,
barAxisMarginStart: Int,
barAxisMarginEnd: Int,
- gravity: Int
+ gravity: Int,
) {
val contextualContainerParams =
FrameLayout.LayoutParams(buttonSize, ViewGroup.LayoutParams.MATCH_PARENT)
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
index 2497fbb98e..a199dba0bd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
@@ -66,7 +66,7 @@ class NavButtonLayoutFactory {
isInSetup: Boolean,
isThreeButtonNav: Boolean,
phoneMode: Boolean,
- @Rotation surfaceRotation: Int
+ @Rotation surfaceRotation: Int,
): NavButtonLayoutter {
val navButtonContainer =
navButtonsView.requireViewById<LinearLayout>(ID_END_NAV_BUTTONS)
@@ -77,6 +77,18 @@ class NavButtonLayoutFactory {
val isPhoneNavMode = phoneMode && isThreeButtonNav
val isPhoneGestureMode = phoneMode && !isThreeButtonNav
return when {
+ isInSetup -> {
+ SetupNavLayoutter(
+ resources,
+ navButtonsView,
+ navButtonContainer,
+ endContextualContainer,
+ startContextualContainer,
+ imeSwitcher,
+ a11yButton,
+ space,
+ )
+ }
isPhoneNavMode -> {
if (!deviceProfile.isLandscape) {
navButtonsView.setIsVertical(false)
@@ -87,7 +99,7 @@ class NavButtonLayoutFactory {
startContextualContainer,
imeSwitcher,
a11yButton,
- space
+ space,
)
} else if (surfaceRotation == ROTATION_90) {
navButtonsView.setIsVertical(true)
@@ -98,7 +110,7 @@ class NavButtonLayoutFactory {
startContextualContainer,
imeSwitcher,
a11yButton,
- space
+ space,
)
} else {
navButtonsView.setIsVertical(true)
@@ -109,36 +121,23 @@ class NavButtonLayoutFactory {
startContextualContainer,
imeSwitcher,
a11yButton,
- space
+ space,
)
}
}
isPhoneGestureMode -> {
PhoneGestureLayoutter(
resources,
- navButtonsView,
navButtonContainer,
endContextualContainer,
startContextualContainer,
imeSwitcher,
a11yButton,
- space
+ space,
)
}
deviceProfile.isTaskbarPresent -> {
return when {
- isInSetup -> {
- SetupNavLayoutter(
- resources,
- navButtonsView,
- navButtonContainer,
- endContextualContainer,
- startContextualContainer,
- imeSwitcher,
- a11yButton,
- space
- )
- }
isKidsMode -> {
KidsNavLayoutter(
resources,
@@ -147,7 +146,7 @@ class NavButtonLayoutFactory {
startContextualContainer,
imeSwitcher,
a11yButton,
- space
+ space,
)
}
else ->
@@ -158,7 +157,7 @@ class NavButtonLayoutFactory {
startContextualContainer,
imeSwitcher,
a11yButton,
- space
+ space,
)
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
index 390ec342e1..e0f2a22b2c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
@@ -17,25 +17,21 @@
package com.android.launcher3.taskbar.navbutton
import android.content.res.Resources
-import android.view.Gravity
import android.view.ViewGroup
-import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.Space
-import com.android.launcher3.DeviceProfile
import com.android.launcher3.taskbar.TaskbarActivityContext
/** Layoutter for showing gesture navigation on phone screen. No buttons here, no-op container */
class PhoneGestureLayoutter(
resources: Resources,
- navButtonsView: NearestTouchFrame,
navBarContainer: LinearLayout,
endContextualContainer: ViewGroup,
startContextualContainer: ViewGroup,
imeSwitcher: ImageView?,
a11yButton: ImageView?,
- space: Space?
+ space: Space?,
) :
AbstractNavButtonLayoutter(
resources,
@@ -44,33 +40,10 @@ class PhoneGestureLayoutter(
startContextualContainer,
imeSwitcher,
a11yButton,
- space
+ space,
) {
- private val mNavButtonsView = navButtonsView
override fun layoutButtons(context: TaskbarActivityContext, isA11yButtonPersistent: Boolean) {
- // TODO: look into if we should use SetupNavLayoutter instead.
- if (!context.isUserSetupComplete) {
- // Since setup wizard only has back button enabled, it looks strange to be
- // end-aligned, so start-align instead.
- val navButtonsLayoutParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
- val navButtonsViewLayoutParams =
- mNavButtonsView.layoutParams as FrameLayout.LayoutParams
- val deviceProfile: DeviceProfile = context.deviceProfile
-
- navButtonsLayoutParams.marginEnd = 0
- navButtonsLayoutParams.gravity = Gravity.START
- context.setTaskbarWindowSize(context.setupWindowSize)
-
- adjustForSetupInPhoneMode(
- navButtonsLayoutParams,
- navButtonsViewLayoutParams,
- deviceProfile
- )
- mNavButtonsView.layoutParams = navButtonsViewLayoutParams
- navButtonContainer.layoutParams = navButtonsLayoutParams
- }
-
endContextualContainer.removeAllViews()
startContextualContainer.removeAllViews()
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
index e032430c1d..eb3fdeb99e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
@@ -29,12 +29,15 @@ import com.android.launcher3.DeviceProfile
import com.android.launcher3.R
import com.android.launcher3.taskbar.TaskbarActivityContext
+const val SUW_THEME_SYSTEM_PROPERTY = "setupwizard.theme"
+const val GLIF_EXPRESSIVE_THEME = "glif_expressive"
+const val GLIF_EXPRESSIVE_LIGHT_THEME = "glif_expressive_light"
const val SQUARE_ASPECT_RATIO_BOTTOM_BOUND = 0.95
const val SQUARE_ASPECT_RATIO_UPPER_BOUND = 1.05
class SetupNavLayoutter(
resources: Resources,
- navButtonsView: NearestTouchFrame,
+ nearestTouchFrame: NearestTouchFrame,
navButtonContainer: LinearLayout,
endContextualContainer: ViewGroup,
startContextualContainer: ViewGroup,
@@ -51,17 +54,19 @@ class SetupNavLayoutter(
a11yButton,
space,
) {
- private val mNavButtonsView = navButtonsView
+ // mNearestTouchFrame is a ViewGroup that contains start, end, nav button ViewGroups
+ private val mNearestTouchFrame = nearestTouchFrame
override fun layoutButtons(context: TaskbarActivityContext, isA11yButtonPersistent: Boolean) {
- val SUWTheme = SystemProperties.get("setupwizard.theme", "")
- if (SUWTheme == "glif_expressive" || SUWTheme == "glif_expressive_light") {
+ val SUWTheme = SystemProperties.get(SUW_THEME_SYSTEM_PROPERTY, "")
+ if (SUWTheme == GLIF_EXPRESSIVE_THEME || SUWTheme == GLIF_EXPRESSIVE_LIGHT_THEME) {
return
}
// Since setup wizard only has back button enabled, it looks strange to be
// end-aligned, so start-align instead.
val navButtonsLayoutParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
- val navButtonsViewLayoutParams = mNavButtonsView.layoutParams as FrameLayout.LayoutParams
+ val navButtonsOverallViewGroupLayoutParams =
+ mNearestTouchFrame.layoutParams as FrameLayout.LayoutParams
val deviceProfile: DeviceProfile = context.deviceProfile
navButtonsLayoutParams.marginEnd = 0
@@ -77,18 +82,14 @@ class SetupNavLayoutter(
) {
navButtonsLayoutParams.marginStart =
resources.getDimensionPixelSize(R.dimen.taskbar_back_button_suw_start_margin)
- navButtonsViewLayoutParams.bottomMargin =
+ navButtonsOverallViewGroupLayoutParams.bottomMargin =
resources.getDimensionPixelSize(R.dimen.taskbar_back_button_suw_bottom_margin)
navButtonsLayoutParams.height =
resources.getDimensionPixelSize(R.dimen.taskbar_back_button_suw_height)
} else {
- adjustForSetupInPhoneMode(
- navButtonsLayoutParams,
- navButtonsViewLayoutParams,
- deviceProfile,
- )
+ adjustForSetupInPhoneMode(navButtonsOverallViewGroupLayoutParams, deviceProfile)
}
- mNavButtonsView.layoutParams = navButtonsViewLayoutParams
+ mNearestTouchFrame.layoutParams = navButtonsOverallViewGroupLayoutParams
navButtonContainer.layoutParams = navButtonsLayoutParams
endContextualContainer.removeAllViews()
diff --git a/quickstep/tests/src/com/android/launcher3/statehandlers/DepthControllerTest.kt b/quickstep/tests/src/com/android/launcher3/statehandlers/DepthControllerTest.kt
new file mode 100644
index 0000000000..17cca0b7d9
--- /dev/null
+++ b/quickstep/tests/src/com/android/launcher3/statehandlers/DepthControllerTest.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2025 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.statehandlers
+
+import android.content.res.Resources
+import android.view.ViewTreeObserver
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.Launcher
+import com.android.launcher3.R
+import com.android.launcher3.dragndrop.DragLayer
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.same
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DepthControllerTest {
+
+ private lateinit var underTest: DepthController
+ @Mock private lateinit var launcher: Launcher
+ @Mock private lateinit var resource: Resources
+ @Mock private lateinit var dragLayer: DragLayer
+ @Mock private lateinit var viewTreeObserver: ViewTreeObserver
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ `when`(launcher.resources).thenReturn(resource)
+ `when`(resource.getInteger(R.integer.max_depth_blur_radius)).thenReturn(30)
+ `when`(launcher.dragLayer).thenReturn(dragLayer)
+ `when`(dragLayer.viewTreeObserver).thenReturn(viewTreeObserver)
+
+ underTest = DepthController(launcher)
+ }
+
+ @Test
+ fun setActivityStarted_add_onDrawListener() {
+ underTest.setActivityStarted(true)
+
+ verify(viewTreeObserver).addOnDrawListener(same(underTest.mOnDrawListener))
+ }
+
+ @Test
+ fun setActivityStopped_not_remove_onDrawListener() {
+ underTest.setActivityStarted(false)
+
+ // Because underTest.mOnDrawListener is never added
+ verifyNoMoreInteractions(viewTreeObserver)
+ }
+
+ @Test
+ fun setActivityStared_then_stopped_remove_onDrawListener() {
+ underTest.setActivityStarted(true)
+ reset(viewTreeObserver)
+
+ underTest.setActivityStarted(false)
+
+ verify(viewTreeObserver).removeOnDrawListener(same(underTest.mOnDrawListener))
+ }
+
+ @Test
+ fun setActivityStared_then_stopped_multiple_times_remove_onDrawListener_once() {
+ underTest.setActivityStarted(true)
+ reset(viewTreeObserver)
+
+ underTest.setActivityStarted(false)
+ underTest.setActivityStarted(false)
+ underTest.setActivityStarted(false)
+
+ // Should just remove mOnDrawListener once
+ verify(viewTreeObserver).removeOnDrawListener(same(underTest.mOnDrawListener))
+ }
+
+ @Test
+ fun test_onInvalidSurface_multiple_times_add_onDrawListener_once() {
+ underTest.onInvalidSurface()
+ underTest.onInvalidSurface()
+ underTest.onInvalidSurface()
+
+ // We should only call addOnDrawListener 1 time
+ verify(viewTreeObserver).addOnDrawListener(same(underTest.mOnDrawListener))
+ }
+}
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
index 61a697503e..8f26795045 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
@@ -316,43 +316,6 @@ class BubbleBarViewAnimatorTest {
}
@Test
- fun animateBubbleInForStashed_showAnimationCanceled() {
- setUpBubbleBar()
-
- val handle = View(context)
- val handleAnimator = PhysicsAnimator.getInstance(handle)
- bubbleStashController.handleAnimator = handleAnimator
-
- val animator =
- BubbleBarViewAnimator(
- bubbleBarView,
- bubbleStashController,
- flyoutController,
- bubbleBarParentViewController,
- onExpanded = emptyRunnable,
- onBubbleBarVisible = emptyRunnable,
- animatorScheduler,
- )
-
- InstrumentationRegistry.getInstrumentation().runOnMainSync {
- animator.animateBubbleInForStashed(bubble, isExpanding = false)
- }
-
- // wait for the animation to start
- InstrumentationRegistry.getInstrumentation().runOnMainSync {}
- PhysicsAnimatorTestUtils.blockUntilFirstAnimationFrameWhereTrue(handleAnimator) { true }
-
- handleAnimator.assertIsRunning()
- assertThat(animator.isAnimating).isTrue()
- assertThat(animatorScheduler.delayedBlock).isNotNull()
-
- handleAnimator.cancel()
- handleAnimator.assertIsNotRunning()
- assertThat(animator.isAnimating).isFalse()
- assertThat(animatorScheduler.delayedBlock).isNull()
- }
-
- @Test
fun animateBubbleInForStashed_autoExpanding() {
setUpBubbleBar()
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 8f116bbd6b..fd8e2f7838 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -45,6 +45,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
@@ -421,7 +422,8 @@ public class LoaderCursor extends CursorWrapper {
) {
boolean isPreArchived = Flags.enableSupportForArchiving()
&& Flags.restoreArchivedAppIconsFromDb()
- && info.isInactiveArchive();
+ && info.isInactiveArchive()
+ && LauncherPrefs.get(mContext).get(LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE);
boolean preArchivedIconNotFound = isPreArchived && !loadIconFromDb(info);
if (preArchivedIconNotFound) {
Log.d(TAG, "loadIconFromDb failed for pre-archived icon, loading from cache."
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
index b848d27cd9..d1292cf0aa 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -21,6 +21,7 @@ import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType
import static androidx.test.InstrumentationRegistry.getContext;
+import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE;
import static com.android.launcher3.LauncherSettings.Favorites.APPWIDGET_ID;
import static com.android.launcher3.LauncherSettings.Favorites.APPWIDGET_PROVIDER;
import static com.android.launcher3.LauncherSettings.Favorites.APPWIDGET_SOURCE;
@@ -69,6 +70,7 @@ import androidx.test.filters.SmallTest;
import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.ItemInfo;
@@ -96,6 +98,7 @@ public class LoaderCursorTest {
private LauncherModelHelper mModelHelper;
private LauncherAppState mApp;
+ private LauncherPrefs mPrefs;
private MatrixCursor mCursor;
private InvariantDeviceProfile mIDP;
@@ -113,6 +116,7 @@ public class LoaderCursorTest {
public void setup() {
mModelHelper = new LauncherModelHelper();
mContext = mModelHelper.sandboxContext;
+ mPrefs = LauncherPrefs.get(mContext);
mIDP = InvariantDeviceProfile.INSTANCE.get(mContext);
mApp = LauncherAppState.getInstance(mContext);
@@ -131,6 +135,7 @@ public class LoaderCursorTest {
@After
public void tearDown() {
+ mPrefs.putSync(IS_FIRST_LOAD_AFTER_RESTORE.to(false));
mCursor.close();
mModelHelper.destroy();
}
@@ -253,8 +258,9 @@ public class LoaderCursorTest {
@Test
@EnableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
- public void ifArchivedWithFlag_whenloadWorkspaceTitleAndIcon_thenLoadIconFromDb() {
+ public void ifArchivedWithFlagAndRestore_whenloadWorkspaceTitleAndIcon_thenLoadIconFromDb() {
// Given
+ mPrefs.putSync(IS_FIRST_LOAD_AFTER_RESTORE.to(true));
initCursor(ITEM_TYPE_APPLICATION, "title");
assertTrue(mLoaderCursor.moveToNext());
WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
@@ -262,6 +268,7 @@ public class LoaderCursorTest {
Bitmap expectedBitmap = LauncherIcons.obtain(mContext)
.createIconBitmap(decodeByteArray(sTestBlob, 0, sTestBlob.length))
.icon;
+
// When
mLoaderCursor.loadWorkspaceTitleAndIcon(false, true, itemInfo);
// Then
@@ -271,6 +278,23 @@ public class LoaderCursorTest {
@Test
@EnableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+ public void ifArchivedWithFlagAndNotRestore_whenloadWorkspaceTitleAndIcon_thenLoadIconFromDb() {
+ // Given
+ mPrefs.putSync(IS_FIRST_LOAD_AFTER_RESTORE.to(false));
+ initCursor(ITEM_TYPE_APPLICATION, "title");
+ assertTrue(mLoaderCursor.moveToNext());
+ WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
+ BitmapInfo original = itemInfo.bitmap;
+ itemInfo.runtimeStatusFlags |= FLAG_ARCHIVED;
+
+ // When
+ mLoaderCursor.loadWorkspaceTitleAndIcon(false, true, itemInfo);
+ // Then
+ assertThat(itemInfo.bitmap).isEqualTo(original);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
public void ifArchivedWithFlag_whenLoadIconFromDb_thenLoadIconFromBlob() {
// Given
initCursor(ITEM_TYPE_APPLICATION, "title");