Merge changes I74af6a72,I603a67a8 into main
* changes:
Animate bubble bar alpha when notif shade opens
Set bubble bar invisible while stashed
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index 9fb5b7b..76b119f 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -434,6 +434,13 @@
bug: "293182501"
}
+flag {
+ name: "enable_recents_window_proto_log"
+ namespace: "launcher"
+ description: "Enables tracking recents window logs in ProtoLog"
+ bug: "292269949"
+}
+
flag {
name: "coordinate_workspace_scale"
diff --git a/aconfig/launcher_overview.aconfig b/aconfig/launcher_overview.aconfig
index 853faf8..c59978f 100644
--- a/aconfig/launcher_overview.aconfig
+++ b/aconfig/launcher_overview.aconfig
@@ -36,7 +36,7 @@
name: "enable_large_desktop_windowing_tile"
namespace: "launcher_overview"
description: "Makes the desktop tiles larger and moves them to the front of the list in Overview."
- bug: "353947137"
+ bug: "357860832"
}
flag {
diff --git a/go/quickstep/src/com/android/launcher3/AppSharing.java b/go/quickstep/src/com/android/launcher3/AppSharing.java
index e15b132..a97fecc 100644
--- a/go/quickstep/src/com/android/launcher3/AppSharing.java
+++ b/go/quickstep/src/com/android/launcher3/AppSharing.java
@@ -37,12 +37,13 @@
import androidx.core.content.FileProvider;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.AppShareabilityChecker;
import com.android.launcher3.model.AppShareabilityJobService;
import com.android.launcher3.model.AppShareabilityManager;
import com.android.launcher3.model.AppShareabilityManager.ShareabilityStatus;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.views.ActivityContext;
@@ -114,17 +115,17 @@
* The Share App system shortcut, used to initiate p2p sharing of a given app
*/
public final class Share extends SystemShortcut<Launcher> {
- private final PopupDataProvider mPopupDataProvider;
private final boolean mSharingEnabledForUser;
private final Set<View> mBoundViews = Collections.newSetFromMap(new WeakHashMap<>());
private boolean mIsEnabled = true;
+ private StatsLogManager mStatsLogManager;
public Share(Launcher target, ItemInfo itemInfo, View originalView) {
super(R.drawable.ic_share, R.string.app_share_drop_target_label, target, itemInfo,
originalView);
- mPopupDataProvider = target.getPopupDataProvider();
-
+ mStatsLogManager = ActivityContext.lookupContext(originalView.getContext())
+ .getStatsLogManager();
mSharingEnabledForUser = bluetoothSharingEnabled(target);
if (!mSharingEnabledForUser) {
setEnabled(false);
@@ -150,8 +151,7 @@
@Override
public void onClick(View view) {
- ActivityContext.lookupContext(view.getContext())
- .getStatsLogManager().logger().log(LAUNCHER_SYSTEM_SHORTCUT_APP_SHARE_TAP);
+ mStatsLogManager.logger().log(LAUNCHER_SYSTEM_SHORTCUT_APP_SHARE_TAP);
if (!mIsEnabled) {
showCannotShareToast(view.getContext());
return;
@@ -240,6 +240,11 @@
public boolean isEnabled() {
return mIsEnabled;
}
+
+ @VisibleForTesting
+ void setStatsLogManager(StatsLogManager statsLogManager) {
+ mStatsLogManager = statsLogManager;
+ }
}
/**
diff --git a/quickstep/res/layout/bubblebar_flyout.xml b/quickstep/res/layout/bubblebar_flyout.xml
index fc1e914..e3338bf 100644
--- a/quickstep/res/layout/bubblebar_flyout.xml
+++ b/quickstep/res/layout/bubblebar_flyout.xml
@@ -19,7 +19,7 @@
xmlns:tools="http://schemas.android.com/tools">
<ImageView
- android:id="@+id/bubble_flyout_avatar"
+ android:id="@+id/bubble_flyout_icon"
android:layout_width="50dp"
android:layout_height="36dp"
android:paddingEnd="@dimen/bubblebar_flyout_avatar_message_space"
@@ -30,14 +30,14 @@
tools:src="#ff0000"/>
<TextView
- android:id="@+id/bubble_flyout_name"
+ android:id="@+id/bubble_flyout_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:fontFamily="@*android:string/config_bodyFontFamilyMedium"
android:maxLines="1"
android:ellipsize="end"
app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toEndOf="@id/bubble_flyout_avatar"
+ app:layout_constraintStart_toEndOf="@id/bubble_flyout_icon"
tools:text="Sender"/>
<TextView
@@ -47,8 +47,8 @@
android:fontFamily="@*android:string/config_bodyFontFamily"
android:maxLines="2"
android:ellipsize="end"
- app:layout_constraintTop_toBottomOf="@id/bubble_flyout_name"
- app:layout_constraintStart_toEndOf="@id/bubble_flyout_avatar"
+ app:layout_constraintTop_toBottomOf="@id/bubble_flyout_title"
+ app:layout_constraintStart_toEndOf="@id/bubble_flyout_icon"
tools:text="This is a message"/>
</merge>
diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
index fb17f15..94a1814 100644
--- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java
+++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
@@ -44,23 +44,31 @@
import androidx.annotation.WorkerThread;
import com.android.launcher3.R;
+import com.android.launcher3.dagger.ApplicationContext;
+import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.popup.RemoteActionShortcut;
import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.util.DaggerSingletonObject;
+import com.android.launcher3.util.DaggerSingletonTracker;
+import com.android.launcher3.util.ExecutorUtil;
import com.android.launcher3.util.Executors;
-import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.launcher3.views.ActivityContext;
+import com.android.quickstep.dagger.QuickstepBaseAppComponent;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import javax.inject.Inject;
+
/**
* Data model for digital wellbeing status of apps.
*/
+@LauncherAppSingleton
public final class WellbeingModel implements SafeCloseable {
private static final String TAG = "WellbeingModel";
private static final int[] RETRY_TIMES_MS = {5000, 15000, 30000};
@@ -75,8 +83,8 @@
private static final String EXTRA_PACKAGES = "packages";
private static final String EXTRA_SUCCESS = "success";
- public static final MainThreadInitializedObject<WellbeingModel> INSTANCE =
- new MainThreadInitializedObject<>(WellbeingModel::new);
+ public static final DaggerSingletonObject<WellbeingModel> INSTANCE =
+ new DaggerSingletonObject<>(QuickstepBaseAppComponent::getWellbeingModel);
private final Context mContext;
private final String mWellbeingProviderPkg;
@@ -93,7 +101,9 @@
private boolean mIsInTest;
- private WellbeingModel(final Context context) {
+ @Inject
+ WellbeingModel(@ApplicationContext final Context context,
+ DaggerSingletonTracker tracker) {
mContext = context;
mWellbeingProviderPkg = mContext.getString(R.string.wellbeing_provider_pkg);
mWorkerHandler = new Handler(TextUtils.isEmpty(mWellbeingProviderPkg)
@@ -112,6 +122,7 @@
}
};
mWorkerHandler.post(this::initializeInBackground);
+ ExecutorUtil.executeSyncOnMainOrFail(() -> tracker.addCloseable(this));
}
@WorkerThread
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 895535e..d1725bc 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -406,6 +406,12 @@
}
};
mSeparateWindowParent.recreateControllers();
+ if (BubbleBarController.isBubbleBarEnabled()) {
+ mNavButtonsView.addOnLayoutChangeListener(
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
+ onLayoutsUpdated()
+ );
+ }
}
private void initButtons(ViewGroup navContainer, ViewGroup endContainer,
@@ -1180,15 +1186,20 @@
/** Adjusts navigation buttons layout accordingly to the bubble bar position. */
@Override
public void onBubbleBarLocationUpdated(BubbleBarLocation location) {
- cancelExistingNavBarAnimation();
- mBubbleBarTargetLocation = location;
+ boolean locationUpdated = location != mBubbleBarTargetLocation;
+ if (locationUpdated) {
+ cancelExistingNavBarAnimation();
+ } else {
+ endExistingAnimation();
+ }
mNavButtonContainer.setTranslationX(getNavBarTranslationX(location));
- mNavButtonContainer.setAlpha(1);
+ mBubbleBarTargetLocation = location;
}
/** Animates navigation buttons accordingly to the bubble bar position. */
@Override
public void onBubbleBarLocationAnimated(BubbleBarLocation location) {
+ if (location == mBubbleBarTargetLocation) return;
cancelExistingNavBarAnimation();
mBubbleBarTargetLocation = location;
int finalX = getNavBarTranslationX(location);
@@ -1199,6 +1210,13 @@
mNavBarLocationAnimator.start();
}
+ private void endExistingAnimation() {
+ if (mNavBarLocationAnimator != null) {
+ mNavBarLocationAnimator.end();
+ mNavBarLocationAnimator = null;
+ }
+ }
+
private void cancelExistingNavBarAnimation() {
if (mNavBarLocationAnimator != null) {
mNavBarLocationAnimator.cancel();
@@ -1240,12 +1258,18 @@
}
/** Adjusts the navigation buttons layout position according to the bubble bar location. */
- public void onTaskbarLayoutChanged() {
- if (mControllers.taskbarViewController.getIconLayoutBounds().isEmpty()) return;
+ public void onLayoutsUpdated() {
+ // no need to do anything if on phone, or if taskbar or navbar views were not placed on
+ // screen.
+ if (mContext.getDeviceProfile().isPhone
+ || mControllers.taskbarViewController.getIconLayoutBounds().isEmpty()
+ || mNavButtonsView.getWidth() == 0) {
+ return;
+ }
if (enableBubbleBarInPersistentTaskBar()
&& mControllers.bubbleControllers.isPresent()) {
if (mBubbleBarTargetLocation == null) {
- // only set bubble bar location if it was not set before, e.g. at device boot
+ // only set bubble bar location if it was not set before
mBubbleBarTargetLocation = mControllers.bubbleControllers.get()
.bubbleBarViewController.getBubbleBarLocation();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 73d7fd0..f9efea9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -278,7 +278,10 @@
// If Bubble bar is present, TaskbarControllers depends on it so build it first.
Optional<BubbleControllers> bubbleControllersOptional = Optional.empty();
BubbleBarController.onTaskbarRecreated();
- if (BubbleBarController.isBubbleBarEnabled() && bubbleBarView != null) {
+ if (BubbleBarController.isBubbleBarEnabled()
+ && !mDeviceProfile.isPhone
+ && bubbleBarView != null
+ ) {
Optional<BubbleStashedHandleViewController> bubbleHandleController = Optional.empty();
Optional<BubbleBarSwipeController> bubbleBarSwipeController = Optional.empty();
if (isTransientTaskbar) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index 5974675..219a24a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -220,11 +220,12 @@
uiController = newUiController;
uiController.init(this);
uiController.updateStateForSysuiFlags(mSharedState.sysuiStateFlags);
- bubbleControllers.ifPresent(bubbleControllers -> {
+ // if bubble controllers are present take bubble bar location, else set it to null
+ bubbleControllers.ifPresentOrElse(bubbleControllers -> {
BubbleBarLocation location =
bubbleControllers.bubbleBarViewController.getBubbleBarLocation();
uiController.onBubbleBarLocationUpdated(location);
- });
+ }, () -> uiController.onBubbleBarLocationUpdated(null));
// Notify that the ui controller has changed
navbarButtonsViewController.onUiControllerChanged();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 8f722f4..8a1d71a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -866,43 +866,49 @@
}
/** Updates launcher home screen appearance accordingly to the bubble bar location. */
- public void onBubbleBarLocationChanged(BubbleBarLocation location, boolean animate) {
- DeviceProfile deviceProfile = mLauncher.getDeviceProfile();
- if (mBubbleBarLocation == location) return;
+ public void onBubbleBarLocationChanged(@Nullable BubbleBarLocation location, boolean animate) {
mBubbleBarLocation = location;
+ if (location == null) {
+ // bubble bar is not present, hence no location, resetting the hotseat
+ updateHotseatAndQsbTranslationX(0, animate);
+ mBubbleBarLocation = null;
+ return;
+ }
+ DeviceProfile deviceProfile = mLauncher.getDeviceProfile();
if (!deviceProfile.shouldAdjustHotseatOnBubblesLocationUpdate(
mControllers.taskbarActivityContext)) {
return;
}
- int targetX = 0;
- if (mBubbleBarLocation != null) {
- boolean isBubblesOnLeft = location.isOnLeft(isRtl(mLauncher.getResources()));
- targetX = deviceProfile.getHotseatTranslationXForBubbleBar(/* isNavbarOnRight= */
- isBubblesOnLeft);
- }
+ boolean isBubblesOnLeft = location.isOnLeft(isRtl(mLauncher.getResources()));
+ int targetX = deviceProfile
+ .getHotseatTranslationXForBubbleBar(/* isNavbarOnRight= */ isBubblesOnLeft);
updateHotseatAndQsbTranslationX(targetX, animate);
}
+ /** Used to translate hotseat and QSB to make room for bubbles. */
private void updateHotseatAndQsbTranslationX(float targetValue, boolean animate) {
// cancel existing animation
if (mHotseatTranslationXAnimation != null) {
mHotseatTranslationXAnimation.cancel();
+ mHotseatTranslationXAnimation = null;
}
- Runnable alignTaskbar = new Runnable() {
+ Runnable postAnimationAction = new Runnable() {
@Override
public void run() {
+ mHotseatTranslationXAnimation = null;
// We only need to align the task bar when on launcher home screen
if (mControllers.taskbarStashController.isOnHome()) {
- DeviceProfile dp = mLauncher.getDeviceProfile();
- mControllers.taskbarViewController
- .setLauncherIconAlignment(/* alignmentRatio = */ 1, dp);
+ mControllers.taskbarViewController.setLauncherIconAlignment(
+ /* alignmentRatio = */ 1,
+ mLauncher.getDeviceProfile()
+ );
}
}
};
Hotseat hotseat = mLauncher.getHotseat();
AnimatorSet translationXAnimation = new AnimatorSet();
- MultiProperty iconsTranslationX = hotseat.getIconsTranslationX(
- Hotseat.ICONS_TRANSLATION_X_NAV_BAR_ALIGNMENT);
+ MultiProperty iconsTranslationX = mLauncher.getHotseat()
+ .getIconsTranslationX(Hotseat.ICONS_TRANSLATION_X_NAV_BAR_ALIGNMENT);
if (animate) {
translationXAnimation.playTogether(iconsTranslationX.animateToValue(targetValue));
} else {
@@ -921,18 +927,17 @@
}
}
if (!animate) {
- alignTaskbar.run();
+ postAnimationAction.run();
return;
}
mHotseatTranslationXAnimation = translationXAnimation;
translationXAnimation.setStartDelay(FADE_OUT_ANIM_POSITION_DURATION_MS);
translationXAnimation.setDuration(FADE_IN_ANIM_ALPHA_DURATION_MS);
translationXAnimation.setInterpolator(Interpolators.EMPHASIZED);
- translationXAnimation.addListener(AnimatorListeners.forEndCallback(alignTaskbar));
+ translationXAnimation.addListener(AnimatorListeners.forEndCallback(postAnimationAction));
translationXAnimation.start();
}
-
private final class TaskBarRecentsAnimationListener implements
RecentsAnimationCallbacks.RecentsAnimationListener {
private final RecentsAnimationCallbacks mCallbacks;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index e522035..21d0cda 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -159,7 +159,9 @@
private final View.OnLayoutChangeListener mTaskbarViewLayoutChangeListener =
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
updateTaskbarIconTranslationXForPinning();
- mControllers.navbarButtonsViewController.onTaskbarLayoutChanged();
+ if (BubbleBarController.isBubbleBarEnabled()) {
+ mControllers.navbarButtonsViewController.onLayoutsUpdated();
+ }
};
// Animation to align icons with Launcher, created lazily. This allows the controller to be
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutMessage.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutMessage.kt
index 7298297..14b456c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutMessage.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutMessage.kt
@@ -18,9 +18,4 @@
import android.graphics.drawable.Drawable
-data class BubbleBarFlyoutMessage(
- val senderAvatar: Drawable?,
- val senderName: CharSequence,
- val message: CharSequence,
- val isGroupChat: Boolean,
-)
+data class BubbleBarFlyoutMessage(val icon: Drawable?, val title: String, val message: String)
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
index 8d84ddf..2022a42 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
@@ -44,11 +44,11 @@
const val MIN_EXPANSION_PROGRESS_FOR_CONTENT_ALPHA = 0.75f
}
- private val sender: TextView by
- lazy(LazyThreadSafetyMode.NONE) { findViewById(R.id.bubble_flyout_name) }
+ private val title: TextView by
+ lazy(LazyThreadSafetyMode.NONE) { findViewById(R.id.bubble_flyout_title) }
- private val avatar: ImageView by
- lazy(LazyThreadSafetyMode.NONE) { findViewById(R.id.bubble_flyout_avatar) }
+ private val icon: ImageView by
+ lazy(LazyThreadSafetyMode.NONE) { findViewById(R.id.bubble_flyout_icon) }
private val message: TextView by
lazy(LazyThreadSafetyMode.NONE) { findViewById(R.id.bubble_flyout_text) }
@@ -171,8 +171,8 @@
/** Sets the data for the flyout and starts playing the expand animation. */
fun showFromCollapsed(flyoutMessage: BubbleBarFlyoutMessage, expandAnimation: () -> Unit) {
- avatar.alpha = 0f
- sender.alpha = 0f
+ icon.alpha = 0f
+ title.alpha = 0f
message.alpha = 0f
setData(flyoutMessage)
val txToCollapsedPosition =
@@ -202,18 +202,18 @@
private fun setData(flyoutMessage: BubbleBarFlyoutMessage) {
// the avatar is only displayed in group chat messages
- if (flyoutMessage.senderAvatar != null && flyoutMessage.isGroupChat) {
- avatar.visibility = VISIBLE
- avatar.setImageDrawable(flyoutMessage.senderAvatar)
+ if (flyoutMessage.icon != null) {
+ icon.visibility = VISIBLE
+ icon.setImageDrawable(flyoutMessage.icon)
} else {
- avatar.visibility = GONE
+ icon.visibility = GONE
}
val minTextViewWidth: Int
val maxTextViewWidth: Int
- if (avatar.visibility == VISIBLE) {
- minTextViewWidth = minFlyoutWidth - avatar.width - flyoutPadding * 2
- maxTextViewWidth = maxFlyoutWidth - avatar.width - flyoutPadding * 2
+ if (icon.visibility == VISIBLE) {
+ minTextViewWidth = minFlyoutWidth - icon.width - flyoutPadding * 2
+ maxTextViewWidth = maxFlyoutWidth - icon.width - flyoutPadding * 2
} else {
// when there's no avatar, the width of the text view is constant, so we're setting the
// min and max to the same value
@@ -221,13 +221,13 @@
maxTextViewWidth = minTextViewWidth
}
- if (flyoutMessage.senderName.isEmpty()) {
- sender.visibility = GONE
+ if (flyoutMessage.title.isEmpty()) {
+ title.visibility = GONE
} else {
- sender.minWidth = minTextViewWidth
- sender.maxWidth = maxTextViewWidth
- sender.text = flyoutMessage.senderName
- sender.visibility = VISIBLE
+ title.minWidth = minTextViewWidth
+ title.maxWidth = maxTextViewWidth
+ title.text = flyoutMessage.title
+ title.visibility = VISIBLE
}
message.minWidth = minTextViewWidth
@@ -240,17 +240,17 @@
expansionProgress = fraction
updateTranslationForAnimation(message)
- updateTranslationForAnimation(sender)
- updateTranslationForAnimation(avatar)
+ updateTranslationForAnimation(title)
+ updateTranslationForAnimation(icon)
// start fading in the content only after we're past the threshold
val alpha =
((expansionProgress - MIN_EXPANSION_PROGRESS_FOR_CONTENT_ALPHA) /
(1f - MIN_EXPANSION_PROGRESS_FOR_CONTENT_ALPHA))
.coerceIn(0f, 1f)
- sender.alpha = alpha
+ title.alpha = alpha
message.alpha = alpha
- avatar.alpha = alpha
+ icon.alpha = alpha
translationZ =
collapsedElevation + (flyoutElevation - collapsedElevation) * expansionProgress
@@ -368,7 +368,7 @@
)
)
backgroundColor = ta.getColor(0, defaultBackgroundColor)
- sender.setTextColor(ta.getColor(1, defaultTextColor))
+ title.setTextColor(ta.getColor(1, defaultTextColor))
message.setTextColor(ta.getColor(2, defaultTextColor))
ta.recycle()
backgroundPaint.color = backgroundColor
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index dc7ed24..3413532 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -237,6 +237,8 @@
getNextStateFlag("STATE_SCALED_CONTROLLER_HOME");
private static final int STATE_SCALED_CONTROLLER_RECENTS =
getNextStateFlag("STATE_SCALED_CONTROLLER_RECENTS");
+ private static final int STATE_PARALLEL_ANIM_FINISHED =
+ getNextStateFlag("STATE_PARALLEL_ANIM_FINISHED");
protected static final int STATE_HANDLER_INVALIDATED =
getNextStateFlag("STATE_HANDLER_INVALIDATED");
@@ -453,7 +455,8 @@
mStateCallback.runOnceAtState(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED
| STATE_SCALED_CONTROLLER_HOME,
this::finishCurrentTransitionToHome);
- mStateCallback.runOnceAtState(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED,
+ mStateCallback.runOnceAtState(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED
+ | STATE_PARALLEL_ANIM_FINISHED,
this::reset);
mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
@@ -1544,9 +1547,12 @@
@Override
public void onAnimationEnd(Animator animation) {
mParallelRunningAnim = null;
+ mStateCallback.setStateOnUiThread(STATE_PARALLEL_ANIM_FINISHED);
}
});
mParallelRunningAnim.start();
+ } else {
+ mStateCallback.setStateOnUiThread(STATE_PARALLEL_ANIM_FINISHED);
}
}
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
index a33e5c0..461f963 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
@@ -27,6 +27,7 @@
import androidx.annotation.UiThread
import androidx.annotation.VisibleForTesting
import com.android.internal.jank.Cuj
+import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
import com.android.launcher3.Flags.enableOverviewCommandHelperTimeout
import com.android.launcher3.PagedView
import com.android.launcher3.logger.LauncherAtom
@@ -215,13 +216,12 @@
}
}
TOGGLE -> {
- val taskView =
- if (recentsView.runningTaskView == null) {
- recentsView.getTaskViewAt(0)
- } else {
- recentsView.nextTaskView ?: recentsView.runningTaskView
- }
- launchTask(recentsView, taskView, command, onCallbackResult)
+ launchTask(
+ recentsView,
+ getNextToggledTaskView(recentsView),
+ command,
+ onCallbackResult,
+ )
}
HOME -> {
recentsView.startHome()
@@ -229,6 +229,27 @@
}
}
+ private fun getNextToggledTaskView(recentsView: RecentsView<*, *>): TaskView? {
+ // When running task view is null we return last large taskView - typically focusView when
+ // grid only is not enabled else last desktop task view.
+ return if (recentsView.runningTaskView == null) {
+ recentsView.lastLargeTaskView ?: recentsView.getTaskViewAt(0)
+ } else {
+ if (
+ enableLargeDesktopWindowingTile() &&
+ recentsView.getTaskViewCount() == recentsView.largeTilesCount &&
+ recentsView.runningTaskView === recentsView.lastLargeTaskView
+ ) {
+ // Enables the toggle when only large tiles are in recents view.
+ // We return previous because unlike small tiles, large tiles are always
+ // on the right hand side.
+ recentsView.previousTaskView ?: recentsView.runningTaskView
+ } else {
+ recentsView.nextTaskView ?: recentsView.runningTaskView
+ }
+ }
+ }
+
private fun launchTask(
recents: RecentsView<*, *>,
taskView: TaskView?,
diff --git a/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java b/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
index f2d5715..eef1f96 100644
--- a/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
+++ b/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
@@ -18,6 +18,7 @@
import com.android.launcher3.dagger.LauncherAppComponent;
import com.android.launcher3.dagger.LauncherBaseAppComponent;
+import com.android.launcher3.model.WellbeingModel;
import com.android.quickstep.logging.SettingsChangeLogger;
/**
@@ -30,4 +31,6 @@
*/
public interface QuickstepBaseAppComponent extends LauncherBaseAppComponent {
SettingsChangeLogger getSettingsChangeLogger();
+
+ WellbeingModel getWellbeingModel();
}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
index 8ce61f5..b5830fd 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
@@ -18,11 +18,9 @@
import android.animation.AnimatorSet
import android.app.ActivityOptions
-import android.content.ComponentName
import android.content.Context
import android.content.LocusId
import android.os.Bundle
-import android.util.Log
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.MotionEvent
@@ -30,7 +28,6 @@
import android.view.RemoteAnimationTarget
import android.view.SurfaceControl
import android.view.View
-import android.view.Window
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
import android.window.RemoteTransition
@@ -63,6 +60,7 @@
import com.android.quickstep.fallback.RecentsState.MODAL_TASK
import com.android.quickstep.fallback.RecentsState.OVERVIEW_SPLIT_SELECT
import com.android.quickstep.util.RecentsAtomicAnimationFactory
+import com.android.quickstep.util.RecentsWindowProtoLogProxy
import com.android.quickstep.util.SplitSelectStateController
import com.android.quickstep.util.TISBindHelper
import com.android.quickstep.views.OverviewActionsView
@@ -71,9 +69,12 @@
import java.util.function.Predicate
/**
- * Class that will manage RecentsView lifecycle within a window and interface correctly
- * where needed. This allows us to run RecentsView in a window where needed.
- * todo: b/365776320, b/365777482
+ * Class that will manage RecentsView lifecycle within a window and interface correctly where
+ * needed. This allows us to run RecentsView in a window where needed. todo: b/365776320,
+ * b/365777482
+ *
+ * To add new protologs, see [RecentsWindowProtoLogProxy]. To enable logging to logcat, see
+ * [QuickstepProtoLogGroup.Constants.DEBUG_RECENTS_WINDOW]
*/
class RecentsWindowManager(context: Context) :
RecentsWindowContext(context), RecentsViewContainer, StatefulContainer<RecentsState> {
@@ -81,12 +82,12 @@
companion object {
private const val HOME_APPEAR_DURATION: Long = 250
private const val TAG = "RecentsWindowManager"
- private const val DEBUG = false
}
protected var recentsView: FallbackRecentsView<RecentsWindowManager>? = null
private val windowContext: Context = createWindowContext(TYPE_APPLICATION_OVERLAY, null)
- private val windowManager: WindowManager = windowContext.getSystemService(WindowManager::class.java)!!
+ private val windowManager: WindowManager =
+ windowContext.getSystemService(WindowManager::class.java)!!
private var layoutInflater: LayoutInflater = LayoutInflater.from(this).cloneInContext(this)
private var stateManager: StateManager<RecentsState, RecentsWindowManager> =
StateManager<RecentsState, RecentsWindowManager>(this, RecentsState.BG_LAUNCHER)
@@ -138,11 +139,11 @@
private val mAnimationToHomeFactory =
RemoteAnimationFactory {
- _: Int,
- appTargets: Array<RemoteAnimationTarget>?,
- wallpaperTargets: Array<RemoteAnimationTarget>?,
- nonAppTargets: Array<RemoteAnimationTarget>?,
- result: LauncherAnimationRunner.AnimationResult? ->
+ _: Int,
+ appTargets: Array<RemoteAnimationTarget>?,
+ wallpaperTargets: Array<RemoteAnimationTarget>?,
+ nonAppTargets: Array<RemoteAnimationTarget>?,
+ result: LauncherAnimationRunner.AnimationResult? ->
val controller =
getStateManager().createAnimationToNewWorkspace(BG_LAUNCHER, HOME_APPEAR_DURATION)
controller.dispatchOnStart()
@@ -171,6 +172,7 @@
}
fun cleanup() {
+ RecentsWindowProtoLogProxy.logCleanup(isShown)
if (isShown) {
windowManager.removeViewImmediate(windowView)
isShown = false
@@ -178,6 +180,7 @@
}
fun startRecentsWindow() {
+ RecentsWindowProtoLogProxy.logStartRecentsWindow(isShown, windowView == null)
if (isShown) return
if (windowView == null) {
windowView = layoutInflater.inflate(R.layout.fallback_recents_activity, null)
@@ -245,37 +248,24 @@
override fun onStateSetStart(state: RecentsState?) {
super.onStateSetStart(state)
- logState(state, "state started:")
+ RecentsWindowProtoLogProxy.logOnStateSetStart(getStateName(state))
}
override fun onStateSetEnd(state: RecentsState?) {
super.onStateSetEnd(state)
- logState(state, "state ended:")
+ RecentsWindowProtoLogProxy.logOnStateSetEnd(getStateName(state))
}
- private fun logState(state: RecentsState?, prefix: String) {
- if (!DEBUG) {
- return
- }
- if (state != null) {
- when (state) {
- DEFAULT -> Log.d(TAG, prefix + "default")
- MODAL_TASK -> {
- Log.d(TAG, prefix + "MODAL_TASK")
- }
- BACKGROUND_APP -> {
- Log.d(TAG, prefix + "BACKGROUND_APP")
- }
- HOME -> {
- Log.d(TAG, prefix + "HOME")
- }
- BG_LAUNCHER -> {
- Log.d(TAG, prefix + "BG_LAUNCHER")
- }
- OVERVIEW_SPLIT_SELECT -> {
- Log.d(TAG, prefix + "OVERVIEW_SPLIT_SELECT")
- }
- }
+ private fun getStateName(state: RecentsState?): String {
+ return when (state) {
+ null -> "NULL"
+ DEFAULT -> "default"
+ MODAL_TASK -> "MODAL_TASK"
+ BACKGROUND_APP -> "BACKGROUND_APP"
+ HOME -> "HOME"
+ BG_LAUNCHER -> "BG_LAUNCHER"
+ OVERVIEW_SPLIT_SELECT -> "OVERVIEW_SPLIT_SELECT"
+ else -> "ordinal=" + state.ordinal
}
}
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 8f579e2..511c989 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -77,6 +77,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.uioverrides.QuickstepLauncher;
@@ -943,7 +944,16 @@
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- controller.finish(true /* toRecents */, null /* onFinishComplete */,
+ controller.finish(
+ true /* toRecents */,
+ () -> {
+ LauncherTaskbarUIController controller =
+ mLauncher.getTaskbarUIController();
+ if (controller != null) {
+ controller.updateTaskbarLauncherStateGoingHome();
+ }
+
+ },
false /* sendUserLeaveHint */);
}
@Override
@@ -951,7 +961,16 @@
SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext())
.onDesktopSplitSelectAnimComplete(mTaskInfo);
}
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mLauncher.getDragLayer().removeView(floatingTaskView);
+ getSplitAnimationController()
+ .removeSplitInstructionsView(mLauncher);
+ resetState();
+ }
});
+ anim.add(getSplitAnimationController()
+ .getShowSplitInstructionsAnim(mLauncher).buildAnim());
anim.buildAnim().start();
}
}
diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
index bdca596..b719ee5 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
@@ -18,6 +18,7 @@
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.annotation.TargetApi;
+import android.app.TaskInfo;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.RectF;
@@ -333,11 +334,16 @@
* context's theme background color.
*/
public static int getDefaultBackgroundColor(
- Context context, RemoteAnimationTarget target) {
- return (target != null && target.taskInfo != null
- && target.taskInfo.taskDescription != null)
- ? target.taskInfo.taskDescription.getBackgroundColor()
- : Themes.getColorBackground(context);
+ Context context, @Nullable RemoteAnimationTarget target) {
+ final int fallbackColor = Themes.getColorBackground(context);
+ if (target == null) {
+ return fallbackColor;
+ }
+ final TaskInfo taskInfo = target.taskInfo;
+ if (taskInfo == null) {
+ return fallbackColor;
+ }
+ return taskInfo.taskDescription.getBackgroundColor();
}
private static void getRelativePosition(View descendant, View ancestor, RectF position) {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 82f08e5..c405080 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -3141,7 +3141,9 @@
// Horizontal grid translation for each task
float[] gridTranslations = new float[taskCount];
- int lastLargeTaskIndex = Integer.MAX_VALUE;
+ TaskView lastLargeTaskView = mUtils.getLastLargeTaskView(getTaskViews());
+ int lastLargeTaskIndex =
+ (lastLargeTaskView == null) ? Integer.MAX_VALUE : indexOfChild(lastLargeTaskView);
Set<Integer> largeTasksIndices = new HashSet<>();
int focusedTaskShift = 0;
int largeTaskWidthAndSpacing = 0;
@@ -3177,7 +3179,6 @@
taskView.setGridTranslationY((mLastComputedTaskSize.height() + taskTopMargin
- taskView.getLayoutParams().height) / 2f);
- lastLargeTaskIndex = i;
largeTasksIndices.add(i);
largeTaskWidthAndSpacing = taskWidthAndSpacing;
@@ -4572,6 +4573,20 @@
}
@Nullable
+ public TaskView getPreviousTaskView() {
+ return getTaskViewAt(getRunningTaskIndex() - 1);
+ }
+
+ @Nullable
+ public TaskView getLastLargeTaskView() {
+ return mUtils.getLastLargeTaskView(getTaskViews());
+ }
+
+ public int getLargeTilesCount() {
+ return mUtils.getLargeTileCount(getTaskViews());
+ }
+
+ @Nullable
public TaskView getCurrentPageTaskView() {
return getTaskViewAt(getCurrentPage());
}
@@ -5404,6 +5419,7 @@
int taskIndex = indexOfChild(taskView);
int centerTaskIndex = getCurrentPage();
+ boolean isRunningTask = taskView.isRunningTask();
float toScale = getMaxScaleForFullScreen();
boolean showAsGrid = showAsGrid();
@@ -5422,13 +5438,16 @@
mTempPointF);
setPivotX(mTempPointF.x);
setPivotY(mTempPointF.y);
- runActionOnRemoteHandles(
- remoteTargetHandle -> {
- remoteTargetHandle.getTaskViewSimulator().setPivotOverride(
- mTempPointF);
- remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(
- false);
- });
+
+ if (!isRunningTask) {
+ runActionOnRemoteHandles(
+ remoteTargetHandle -> {
+ remoteTargetHandle.getTaskViewSimulator().setPivotOverride(
+ mTempPointF);
+ remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(
+ false);
+ });
+ }
}
});
} else if (!showAsGrid) {
diff --git a/quickstep/src_protolog/com/android/quickstep/util/QuickstepProtoLogGroup.java b/quickstep/src_protolog/com/android/quickstep/util/QuickstepProtoLogGroup.java
index d0863f8..7b81b9a 100644
--- a/quickstep/src_protolog/com/android/quickstep/util/QuickstepProtoLogGroup.java
+++ b/quickstep/src_protolog/com/android/quickstep/util/QuickstepProtoLogGroup.java
@@ -26,7 +26,8 @@
/** Enums used to interface with the ProtoLog API. */
public enum QuickstepProtoLogGroup implements IProtoLogGroup {
- ACTIVE_GESTURE_LOG(true, true, false, "ActiveGestureLog");
+ ACTIVE_GESTURE_LOG(true, true, false, "ActiveGestureLog"),
+ RECENTS_WINDOW(true, true, Constants.DEBUG_RECENTS_WINDOW, "RecentsWindow");
private final boolean mEnabled;
private volatile boolean mLogToProto;
@@ -95,6 +96,8 @@
private static final class Constants {
+ private static final boolean DEBUG_RECENTS_WINDOW = false;
+
private static final int LOG_START_ID =
(int) (UUID.nameUUIDFromBytes(QuickstepProtoLogGroup.class.getName().getBytes())
.getMostSignificantBits() % Integer.MAX_VALUE);
diff --git a/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java b/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java
new file mode 100644
index 0000000..f54ad67
--- /dev/null
+++ b/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import static com.android.launcher3.Flags.enableRecentsWindowProtoLog;
+import static com.android.quickstep.util.QuickstepProtoLogGroup.RECENTS_WINDOW;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.protolog.ProtoLog;
+import com.android.internal.protolog.common.IProtoLogGroup;
+
+/**
+ * Proxy class used for Recents Window ProtoLog support.
+ * <p>
+ * This file will have all of its static strings in the
+ * {@link ProtoLog#d(IProtoLogGroup, String, Object...)} calls replaced by dynamic code/strings.
+ * <p>
+ * When a new Recents Window log needs to be added to the codebase, add it here under a new unique
+ * method. Or, if an existing entry needs to be modified, simply update it here.
+ */
+public class RecentsWindowProtoLogProxy {
+
+ public static void logOnStateSetStart(@NonNull String stateName) {
+ if (!enableRecentsWindowProtoLog()) return;
+ ProtoLog.d(RECENTS_WINDOW, "onStateSetStart: %s", stateName);
+ }
+
+ public static void logOnStateSetEnd(@NonNull String stateName) {
+ if (!enableRecentsWindowProtoLog()) return;
+ ProtoLog.d(RECENTS_WINDOW, "onStateSetEnd: %s", stateName);
+ }
+
+ public static void logStartRecentsWindow(boolean isShown, boolean windowViewIsNull) {
+ if (!enableRecentsWindowProtoLog()) return;
+ ProtoLog.d(RECENTS_WINDOW,
+ "Starting recents window: isShow= %b, windowViewIsNull=%b",
+ isShown,
+ windowViewIsNull);
+ }
+
+ public static void logCleanup(boolean isShown) {
+ if (!enableRecentsWindowProtoLog()) return;
+ ProtoLog.d(RECENTS_WINDOW, "Cleaning up recents window: isShow= %b", isShown);
+ }
+}
diff --git a/quickstep/testing/com/android/launcher3/taskbar/bubbles/testing/FakeBubbleViewFactory.kt b/quickstep/testing/com/android/launcher3/taskbar/bubbles/testing/FakeBubbleViewFactory.kt
index 37a07c3..473d8ef 100644
--- a/quickstep/testing/com/android/launcher3/taskbar/bubbles/testing/FakeBubbleViewFactory.kt
+++ b/quickstep/testing/com/android/launcher3/taskbar/bubbles/testing/FakeBubbleViewFactory.kt
@@ -54,7 +54,19 @@
val flags =
if (suppressNotification) Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION else 0
val bubbleInfo =
- BubbleInfo(key, flags, null, null, 0, context.packageName, null, null, false, true)
+ BubbleInfo(
+ key,
+ flags,
+ null,
+ null,
+ 0,
+ context.packageName,
+ null,
+ null,
+ false,
+ true,
+ null,
+ )
val bubbleView = inflater.inflate(R.layout.bubblebar_item_view, parent, false) as BubbleView
val dotPath =
PathParser.createPathFromPathData(
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutViewScreenshotTest.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutViewScreenshotTest.kt
index 6aba6a3..11c7fe9 100644
--- a/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutViewScreenshotTest.kt
+++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutViewScreenshotTest.kt
@@ -63,12 +63,7 @@
val flyout =
BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = false))
flyout.showFromCollapsed(
- BubbleBarFlyoutMessage(
- senderAvatar = null,
- senderName = "sender",
- message = "message",
- isGroupChat = false,
- )
+ BubbleBarFlyoutMessage(icon = null, title = "sender", message = "message")
) {}
flyout.updateExpansionProgress(1f)
flyout
@@ -82,12 +77,7 @@
val flyout =
BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = true))
flyout.showFromCollapsed(
- BubbleBarFlyoutMessage(
- senderAvatar = null,
- senderName = "sender",
- message = "message",
- isGroupChat = false,
- )
+ BubbleBarFlyoutMessage(icon = null, title = "sender", message = "message")
) {}
flyout.updateExpansionProgress(1f)
flyout
@@ -102,10 +92,9 @@
BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = true))
flyout.showFromCollapsed(
BubbleBarFlyoutMessage(
- senderAvatar = null,
- senderName = "sender",
+ icon = null,
+ title = "sender",
message = "really, really, really, really, really long message. like really.",
- isGroupChat = false,
)
) {}
flyout.updateExpansionProgress(1f)
@@ -121,10 +110,9 @@
BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = false))
flyout.showFromCollapsed(
BubbleBarFlyoutMessage(
- senderAvatar = ColorDrawable(Color.RED),
- senderName = "sender",
+ icon = ColorDrawable(Color.RED),
+ title = "sender",
message = "message",
- isGroupChat = true,
)
) {}
flyout.updateExpansionProgress(1f)
@@ -140,10 +128,9 @@
BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = true))
flyout.showFromCollapsed(
BubbleBarFlyoutMessage(
- senderAvatar = ColorDrawable(Color.RED),
- senderName = "sender",
+ icon = ColorDrawable(Color.RED),
+ title = "sender",
message = "message",
- isGroupChat = true,
)
) {}
flyout.updateExpansionProgress(1f)
@@ -159,10 +146,9 @@
BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = true))
flyout.showFromCollapsed(
BubbleBarFlyoutMessage(
- senderAvatar = ColorDrawable(Color.RED),
- senderName = "sender",
+ icon = ColorDrawable(Color.RED),
+ title = "sender",
message = "really, really, really, really, really long message. like really.",
- isGroupChat = true,
)
) {}
flyout.updateExpansionProgress(1f)
@@ -178,10 +164,9 @@
BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = true))
flyout.showFromCollapsed(
BubbleBarFlyoutMessage(
- senderAvatar = ColorDrawable(Color.RED),
- senderName = "sender",
+ icon = ColorDrawable(Color.RED),
+ title = "sender",
message = "collapsed on left",
- isGroupChat = true,
)
) {}
flyout.updateExpansionProgress(0f)
@@ -197,10 +182,9 @@
BubbleBarFlyoutView(context, FakeBubbleBarFlyoutPositioner(isOnLeft = false))
flyout.showFromCollapsed(
BubbleBarFlyoutMessage(
- senderAvatar = ColorDrawable(Color.RED),
- senderName = "sender",
+ icon = ColorDrawable(Color.RED),
+ title = "sender",
message = "collapsed on right",
- isGroupChat = true,
)
) {}
flyout.updateExpansionProgress(0f)
@@ -222,10 +206,9 @@
)
flyout.showFromCollapsed(
BubbleBarFlyoutMessage(
- senderAvatar = ColorDrawable(Color.RED),
- senderName = "sender",
+ icon = ColorDrawable(Color.RED),
+ title = "sender",
message = "expanded 90% on left",
- isGroupChat = true,
)
) {}
flyout.updateExpansionProgress(0.9f)
@@ -247,10 +230,9 @@
)
flyout.showFromCollapsed(
BubbleBarFlyoutMessage(
- senderAvatar = ColorDrawable(Color.RED),
- senderName = "sender",
+ icon = ColorDrawable(Color.RED),
+ title = "sender",
message = "expanded 80% on right",
- isGroupChat = true,
)
) {}
flyout.updateExpansionProgress(0.8f)
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt
index e736446..d7ce4ed 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarStashControllerTest.kt
@@ -35,7 +35,8 @@
import com.android.launcher3.taskbar.TaskbarStashController.TRANSIENT_TASKBAR_STASH_ALPHA_DURATION
import com.android.launcher3.taskbar.TaskbarStashController.TRANSIENT_TASKBAR_STASH_DURATION
import com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_STASH
-import com.android.launcher3.taskbar.bubbles.BubbleControllers
+import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
+import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
import com.android.launcher3.taskbar.rules.TaskbarModeRule
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.PINNED
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.THREE_BUTTONS
@@ -52,7 +53,6 @@
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING
import com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_BAR
import com.google.common.truth.Truth.assertThat
-import java.util.Optional
import org.junit.After
import org.junit.Before
import org.junit.Rule
@@ -60,6 +60,7 @@
import org.junit.runner.RunWith
@RunWith(LauncherMultivalentJUnit::class)
+@EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
@EmulatedDevices(["pixelTablet2023"])
class TaskbarStashControllerTest {
private val context = TaskbarWindowSandboxContext.create(getInstrumentation().targetContext)
@@ -74,7 +75,8 @@
@InjectController lateinit var stashedHandleViewController: StashedHandleViewController
@InjectController lateinit var dragLayerController: TaskbarDragLayerController
@InjectController lateinit var autohideSuspendController: TaskbarAutohideSuspendController
- @InjectController lateinit var bubbleControllers: Optional<BubbleControllers>
+ @InjectController lateinit var bubbleBarViewController: BubbleBarViewController
+ @InjectController lateinit var bubbleStashController: BubbleStashController
private val activityContext by taskbarUnitTestRule::activityContext
@@ -420,60 +422,55 @@
}
@Test
- @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
@TaskbarMode(TRANSIENT)
fun testUpdateAndAnimateTransientTaskbar_unstashTaskbarWithBubbles_bubbleBarUnstashes() {
getInstrumentation().runOnMainSync {
- bubbleControllers.get().bubbleBarViewController.setHiddenForBubbles(false)
- bubbleControllers.get().bubbleStashController.stashBubbleBarImmediate()
+ bubbleBarViewController.setHiddenForBubbles(false)
+ bubbleStashController.stashBubbleBarImmediate()
stashController.updateAndAnimateTransientTaskbar(false, true)
}
- assertThat(bubbleControllers.get().bubbleStashController.isStashed).isFalse()
+ assertThat(bubbleStashController.isStashed).isFalse()
}
@Test
- @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
@TaskbarMode(TRANSIENT)
fun testUpdateAndAnimateTransientTaskbar_unstashTaskbarWithoutBubbles_bubbleBarStashed() {
getInstrumentation().runOnMainSync {
- bubbleControllers.get().bubbleBarViewController.setHiddenForBubbles(false)
- bubbleControllers.get().bubbleStashController.stashBubbleBarImmediate()
+ bubbleBarViewController.setHiddenForBubbles(false)
+ bubbleStashController.stashBubbleBarImmediate()
stashController.updateAndAnimateTransientTaskbar(false, false)
}
- assertThat(bubbleControllers.get().bubbleStashController.isStashed).isTrue()
+ assertThat(bubbleStashController.isStashed).isTrue()
}
@Test
- @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
@TaskbarMode(TRANSIENT)
fun testUpdateAndAnimateTransientTaskbar_stashTaskbarWithBubbles_bubbleBarStashes() {
getInstrumentation().runOnMainSync {
- bubbleControllers.get().bubbleBarViewController.setHiddenForBubbles(false)
- bubbleControllers.get().bubbleStashController.showBubbleBarImmediate()
+ bubbleBarViewController.setHiddenForBubbles(false)
+ bubbleStashController.showBubbleBarImmediate()
stashController.updateAndAnimateTransientTaskbar(true, true)
}
- assertThat(bubbleControllers.get().bubbleStashController.isStashed).isTrue()
+ assertThat(bubbleStashController.isStashed).isTrue()
}
@Test
- @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
@TaskbarMode(TRANSIENT)
fun testUpdateAndAnimateTransientTaskbar_stashTaskbarWithoutBubbles_bubbleBarUnstashed() {
getInstrumentation().runOnMainSync {
- bubbleControllers.get().bubbleBarViewController.setHiddenForBubbles(false)
- bubbleControllers.get().bubbleStashController.showBubbleBarImmediate()
+ bubbleBarViewController.setHiddenForBubbles(false)
+ bubbleStashController.showBubbleBarImmediate()
stashController.updateAndAnimateTransientTaskbar(true, false)
}
- assertThat(bubbleControllers.get().bubbleStashController.isStashed).isFalse()
+ assertThat(bubbleStashController.isStashed).isFalse()
}
@Test
- @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
@TaskbarMode(TRANSIENT)
fun testUpdateAndAnimateTransientTaskbar_bubbleBarExpandedBeforeTimeout_expandedAfterwards() {
getInstrumentation().runOnMainSync {
- bubbleControllers.get().bubbleBarViewController.setHiddenForBubbles(false)
- bubbleControllers.get().bubbleBarViewController.isExpanded = true
+ bubbleBarViewController.setHiddenForBubbles(false)
+ bubbleBarViewController.isExpanded = true
stashController.updateAndAnimateTransientTaskbar(false)
animatorTestRule.advanceTimeBy(stashController.stashDuration)
}
@@ -483,7 +480,7 @@
stashController.timeoutAlarm.finishAlarm()
animatorTestRule.advanceTimeBy(stashController.stashDuration)
}
- assertThat(bubbleControllers.get().bubbleBarViewController.isExpanded).isTrue()
+ assertThat(bubbleBarViewController.isExpanded).isTrue()
}
@Test
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleViewTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleViewTest.kt
index 94f9cf5..2caff01 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleViewTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/BubbleViewTest.kt
@@ -65,7 +65,19 @@
overflowView.setOverflow(BubbleBarOverflow(overflowView), bitmap)
val bubbleInfo =
- BubbleInfo("key", 0, null, null, 0, context.packageName, null, null, false, true)
+ BubbleInfo(
+ "key",
+ 0,
+ null,
+ null,
+ 0,
+ context.packageName,
+ null,
+ null,
+ false,
+ true,
+ null,
+ )
bubbleView = inflater.inflate(R.layout.bubblebar_item_view, null, false) as BubbleView
bubble =
BubbleBarBubble(bubbleInfo, bubbleView, bitmap, bitmap, Color.WHITE, Path(), "")
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
index 84e872d..3913165 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
@@ -86,7 +86,7 @@
bubbleBarView,
bubbleStashController,
onExpandedNoOp,
- animatorScheduler
+ animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -135,7 +135,7 @@
bubbleBarView,
bubbleStashController,
onExpandedNoOp,
- animatorScheduler
+ animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -183,7 +183,7 @@
bubbleBarView,
bubbleStashController,
onExpandedNoOp,
- animatorScheduler
+ animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -228,7 +228,7 @@
bubbleBarView,
bubbleStashController,
onExpandedNoOp,
- animatorScheduler
+ animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -274,7 +274,7 @@
bubbleBarView,
bubbleStashController,
onExpandedNoOp,
- animatorScheduler
+ animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -311,7 +311,7 @@
bubbleBarView,
bubbleStashController,
onExpanded,
- animatorScheduler
+ animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -355,7 +355,7 @@
bubbleBarView,
bubbleStashController,
onExpanded,
- animatorScheduler
+ animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -405,7 +405,7 @@
bubbleBarView,
bubbleStashController,
onExpanded,
- animatorScheduler
+ animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -454,7 +454,7 @@
bubbleBarView,
bubbleStashController,
onExpandedNoOp,
- animatorScheduler
+ animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -504,7 +504,7 @@
bubbleBarView,
bubbleStashController,
onExpanded,
- animatorScheduler
+ animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -538,7 +538,7 @@
bubbleBarView,
bubbleStashController,
onExpandedNoOp,
- animatorScheduler
+ animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -577,7 +577,7 @@
bubbleBarView,
bubbleStashController,
onExpanded,
- animatorScheduler
+ animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -625,7 +625,7 @@
bubbleBarView,
bubbleStashController,
onExpanded,
- animatorScheduler
+ animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -666,7 +666,7 @@
bubbleBarView,
bubbleStashController,
onExpandedNoOp,
- animatorScheduler
+ animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -713,7 +713,7 @@
bubbleBarView,
bubbleStashController,
onExpanded,
- animatorScheduler
+ animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -760,7 +760,7 @@
bubbleBarView,
bubbleStashController,
onExpanded,
- animatorScheduler
+ animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -818,7 +818,7 @@
bubbleBarView,
bubbleStashController,
onExpanded,
- animatorScheduler
+ animatorScheduler,
)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -870,7 +870,19 @@
bubbleBarView.addView(overflowView)
val bubbleInfo =
- BubbleInfo("key", 0, null, null, 0, context.packageName, null, null, false, true)
+ BubbleInfo(
+ "key",
+ 0,
+ null,
+ null,
+ 0,
+ context.packageName,
+ null,
+ null,
+ false,
+ true,
+ null,
+ )
bubbleView =
inflater.inflate(R.layout.bubblebar_item_view, bubbleBarView, false) as BubbleView
bubble =
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt
index d857ae5..fdafce0 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt
@@ -39,8 +39,7 @@
private lateinit var flyoutController: BubbleBarFlyoutController
private lateinit var flyoutContainer: FrameLayout
private val context = ApplicationProvider.getApplicationContext<Context>()
- private val flyoutMessage =
- BubbleBarFlyoutMessage(senderAvatar = null, "sender name", "message", isGroupChat = false)
+ private val flyoutMessage = BubbleBarFlyoutMessage(icon = null, "sender name", "message")
private var onLeft = true
@Before
@@ -87,7 +86,7 @@
flyoutController.setUpFlyout(flyoutMessage)
assertThat(flyoutContainer.childCount).isEqualTo(1)
val flyout = flyoutContainer.getChildAt(0)
- val sender = flyout.findViewById<TextView>(R.id.bubble_flyout_name)
+ val sender = flyout.findViewById<TextView>(R.id.bubble_flyout_title)
assertThat(sender.text).isEqualTo("sender name")
val message = flyout.findViewById<TextView>(R.id.bubble_flyout_text)
assertThat(message.text).isEqualTo("message")
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
index cb5e464..b0d706f 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
@@ -28,9 +28,11 @@
import com.android.launcher3.LauncherAppState
import com.android.launcher3.statehandlers.DesktopVisibilityController
import com.android.launcher3.taskbar.TaskbarActivityContext
+import com.android.launcher3.taskbar.TaskbarControllers
import com.android.launcher3.taskbar.TaskbarManager
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks
import com.android.launcher3.taskbar.TaskbarViewController
+import com.android.launcher3.taskbar.bubbles.BubbleControllers
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
import com.android.launcher3.util.LauncherMultivalentJUnit.Companion.isRunningInRobolectric
@@ -38,6 +40,9 @@
import com.android.quickstep.AllAppsActionManager
import com.android.quickstep.TouchInteractionService
import com.android.quickstep.TouchInteractionService.TISBinder
+import java.lang.reflect.Field
+import java.lang.reflect.ParameterizedType
+import java.util.Optional
import org.junit.Assume.assumeTrue
import org.junit.rules.RuleChain
import org.junit.rules.TestRule
@@ -180,19 +185,38 @@
fun recreateTaskbar() = instrumentation.runOnMainSync { taskbarManager.recreateTaskbar() }
private fun injectControllers() {
- val controllers = activityContext.controllers
- val controllerFieldsByType = controllers.javaClass.fields.associateBy { it.type }
+ val bubbleControllerTypes =
+ BubbleControllers::class.java.fields.map { f ->
+ if (f.type == Optional::class.java) {
+ (f.genericType as ParameterizedType).actualTypeArguments[0] as Class<*>
+ } else {
+ f.type
+ }
+ }
testInstance.javaClass.fields
.filter { it.isAnnotationPresent(InjectController::class.java) }
.forEach {
- it.set(
- testInstance,
- controllerFieldsByType[it.type]?.get(controllers)
- ?: throw NoSuchElementException("Failed to find controller for ${it.type}"),
- )
+ val controllers: Any =
+ if (it.type in bubbleControllerTypes) {
+ activityContext.controllers.bubbleControllers.orElseThrow {
+ NoSuchElementException("Bubble controllers are not initialized")
+ }
+ } else {
+ activityContext.controllers
+ }
+ injectController(it, testInstance, controllers)
}
}
+ private fun injectController(field: Field, testInstance: Any, controllers: Any) {
+ val controllerFieldsByType = controllers.javaClass.fields.associateBy { it.type }
+ field.set(
+ testInstance,
+ controllerFieldsByType[field.type]?.get(controllers)
+ ?: throw NoSuchElementException("Failed to find controller for ${field.type}"),
+ )
+ }
+
/**
* Annotates test controller fields to inject the corresponding controllers from the current
* [TaskbarControllers] instance.
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRuleTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRuleTest.kt
index 5d4fdc5..52ca78d 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRuleTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRuleTest.kt
@@ -16,18 +16,24 @@
package com.android.launcher3.taskbar.rules
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.taskbar.TaskbarKeyguardController
import com.android.launcher3.taskbar.TaskbarManager
import com.android.launcher3.taskbar.TaskbarStashController
+import com.android.launcher3.taskbar.bubbles.BubbleBarController
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.NavBarKidsMode
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.UserSetupMode
import com.android.launcher3.util.LauncherMultivalentJUnit
import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
+import com.android.wm.shell.Flags
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertThrows
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.Description
import org.junit.runner.RunWith
@@ -39,6 +45,8 @@
private val context = TaskbarWindowSandboxContext.create(getInstrumentation().targetContext)
+ @get:Rule(order = 0) val setFlagsRule = SetFlagsRule()
+
@Test
fun testSetup_taskbarInitialized() {
onSetup { assertThat(activityContext).isInstanceOf(TaskbarActivityContext::class.java) }
@@ -127,6 +135,44 @@
}
}
+ @EnableFlags(Flags.FLAG_ENABLE_BUBBLE_BAR)
+ @Test
+ fun testInjectBubbleController_bubbleFlagOn_isInjected() {
+ val testClass =
+ object {
+ @InjectController lateinit var controller: BubbleBarController
+ val isInjected: Boolean
+ get() = ::controller.isInitialized
+ }
+
+ TaskbarUnitTestRule(testClass, context).apply(EMPTY_STATEMENT, DESCRIPTION).evaluate()
+
+ onSetup(TaskbarUnitTestRule(testClass, context)) {
+ assertThat(testClass.isInjected).isTrue()
+ }
+ }
+
+ @DisableFlags(Flags.FLAG_ENABLE_BUBBLE_BAR)
+ @Test
+ fun testInjectBubbleController_bubbleFlagOff_exceptionThrown() {
+ val testClass =
+ object {
+ @InjectController lateinit var controller: BubbleBarController
+ }
+
+ // We cannot use #assertThrows because we also catch an assumption violated exception when
+ // running #evaluate on devices that do not support Taskbar.
+ val result =
+ try {
+ TaskbarUnitTestRule(testClass, context)
+ .apply(EMPTY_STATEMENT, DESCRIPTION)
+ .evaluate()
+ } catch (e: NoSuchElementException) {
+ e
+ }
+ assertThat(result).isInstanceOf(NoSuchElementException::class.java)
+ }
+
@Test
fun testUserSetupMode_default_isComplete() {
onSetup { assertThat(activityContext.isUserSetupComplete).isTrue() }
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
index 87a7cda..0bf9886 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
@@ -16,15 +16,22 @@
package com.android.quickstep;
+import static com.android.quickstep.AbsSwipeUpHandler.STATE_HANDLER_INVALIDATED;
+
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertTrue;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
@@ -38,14 +45,13 @@
import android.view.ViewTreeObserver;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Flags;
import com.android.launcher3.LauncherRootView;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.statemanager.BaseState;
-import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.statemanager.StatefulContainer;
import com.android.launcher3.util.SystemUiController;
import com.android.quickstep.fallback.window.RecentsWindowManager;
@@ -57,6 +63,7 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -65,11 +72,11 @@
import java.util.HashMap;
public abstract class AbsSwipeUpHandlerTestCase<
- RECENTS_CONTAINER extends Context & RecentsViewContainer & StatefulContainer<STATE>,
- STATE extends BaseState<STATE>, RECENTS_VIEW extends RecentsView<RECENTS_CONTAINER, STATE>,
- ACTIVITY_TYPE extends StatefulActivity<STATE> & RecentsViewContainer,
- ACTIVITY_INTERFACE extends BaseActivityInterface<STATE, ACTIVITY_TYPE>,
- SWIPE_HANDLER extends AbsSwipeUpHandler<RECENTS_CONTAINER, RECENTS_VIEW, STATE>> {
+ STATE_TYPE extends BaseState<STATE_TYPE>,
+ RECENTS_CONTAINER extends Context & RecentsViewContainer & StatefulContainer<STATE_TYPE>,
+ RECENTS_VIEW extends RecentsView<RECENTS_CONTAINER, STATE_TYPE>,
+ SWIPE_HANDLER extends AbsSwipeUpHandler<RECENTS_CONTAINER, RECENTS_VIEW, STATE_TYPE>,
+ CONTAINER_INTERFACE extends BaseContainerInterface<STATE_TYPE, RECENTS_CONTAINER>> {
protected final Context mContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
@@ -106,13 +113,12 @@
/* minimizedHomeBounds= */ null,
new Bundle());
- protected RecentsWindowManager mRecentsWindowManager;
protected TaskAnimationManager mTaskAnimationManager;
- @Mock protected ACTIVITY_INTERFACE mActivityInterface;
+ @Mock protected CONTAINER_INTERFACE mActivityInterface;
@Mock protected ActivityInitListener<?> mActivityInitListener;
@Mock protected RecentsAnimationController mRecentsAnimationController;
- @Mock protected STATE mState;
+ @Mock protected STATE_TYPE mState;
@Mock protected ViewTreeObserver mViewTreeObserver;
@Mock protected DragLayer mDragLayer;
@Mock protected LauncherRootView mRootView;
@@ -123,16 +129,6 @@
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Before
- public void setUpTaskAnimationManager() {
- runOnMainSync(() -> {
- if(Flags.enableFallbackOverviewInWindow()){
- mRecentsWindowManager = new RecentsWindowManager(mContext);
- }
- mTaskAnimationManager = new TaskAnimationManager(mContext, mRecentsWindowManager);
- });
- }
-
- @Before
public void setUpRunningTaskInfo() {
mRunningTaskInfo.baseIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
@@ -162,6 +158,7 @@
@Before
public void setUpRecentsContainer() {
+ mTaskAnimationManager = new TaskAnimationManager(mContext, getRecentsWindowManager());
RecentsViewContainer recentsContainer = getRecentsContainer();
RECENTS_VIEW recentsView = getRecentsView();
@@ -221,7 +218,7 @@
runOnMainSync(() -> {
absSwipeUpHandler.startNewTask(unused -> {});
- verify(mRecentsAnimationController).finish(anyBoolean(), any());
+ verifyRecentsAnimationFinishedAndCallCallback();
});
}
@@ -231,10 +228,57 @@
runOnMainSync(() -> {
verify(mRecentsAnimationController).detachNavigationBarFromApp(true);
- verify(mRecentsAnimationController).finish(anyBoolean(), any(), anyBoolean());
+ verifyRecentsAnimationFinishedAndCallCallback();
});
}
+ @Test
+ public void testHomeGesture_invalidatesHandlerAfterParallelAnim() {
+ ValueAnimator parallelAnim = new ValueAnimator();
+ parallelAnim.setRepeatCount(ValueAnimator.INFINITE);
+ when(mActivityInterface.getParallelAnimationToLauncher(any(), anyLong(), any()))
+ .thenReturn(parallelAnim);
+ SWIPE_HANDLER handler = createSwipeUpHandlerForGesture(GestureState.GestureEndTarget.HOME);
+ runOnMainSync(() -> {
+ parallelAnim.start();
+ verifyRecentsAnimationFinishedAndCallCallback();
+ assertFalse(handler.mStateCallback.hasStates(STATE_HANDLER_INVALIDATED));
+ parallelAnim.end();
+ assertTrue(handler.mStateCallback.hasStates(STATE_HANDLER_INVALIDATED));
+ });
+ }
+
+ @Test
+ public void testHomeGesture_invalidatesHandlerIfNoParallelAnim() {
+ when(mActivityInterface.getParallelAnimationToLauncher(any(), anyLong(), any()))
+ .thenReturn(null);
+ SWIPE_HANDLER handler = createSwipeUpHandlerForGesture(GestureState.GestureEndTarget.HOME);
+ runOnMainSync(() -> {
+ verifyRecentsAnimationFinishedAndCallCallback();
+ assertTrue(handler.mStateCallback.hasStates(STATE_HANDLER_INVALIDATED));
+ });
+ }
+
+ /**
+ * Verifies that RecentsAnimationController#finish() is called, and captures and runs any
+ * callback that was passed to it. This ensures that STATE_CURRENT_TASK_FINISHED is correctly
+ * set for example.
+ */
+ private void verifyRecentsAnimationFinishedAndCallCallback() {
+ ArgumentCaptor<Runnable> finishCallback = ArgumentCaptor.forClass(Runnable.class);
+ // Check if the 2 parameter method is called.
+ verify(mRecentsAnimationController, atLeast(0)).finish(
+ anyBoolean(), finishCallback.capture());
+ if (finishCallback.getAllValues().isEmpty()) {
+ // Check if the 3 parameter method is called.
+ verify(mRecentsAnimationController).finish(
+ anyBoolean(), finishCallback.capture(), anyBoolean());
+ }
+ if (finishCallback.getValue() != null) {
+ finishCallback.getValue().run();
+ }
+ }
+
private SWIPE_HANDLER createSwipeUpHandlerForGesture(GestureState.GestureEndTarget endTarget) {
boolean isQuickSwitch = endTarget == GestureState.GestureEndTarget.NEW_TASK;
@@ -263,13 +307,12 @@
private void onRecentsAnimationStart(SWIPE_HANDLER absSwipeUpHandler) {
when(mActivityInterface.getOverviewWindowBounds(any(), any())).thenReturn(new Rect());
- doNothing().when(mActivityInterface).setOnDeferredActivityLaunchCallback(any());
runOnMainSync(() -> absSwipeUpHandler.onRecentsAnimationStart(
mRecentsAnimationController, mRecentsAnimationTargets));
}
- private static void runOnMainSync(Runnable runnable) {
+ protected static void runOnMainSync(Runnable runnable) {
InstrumentationRegistry.getInstrumentation().runOnMainSync(runnable);
}
@@ -278,6 +321,11 @@
return createSwipeHandler(SystemClock.uptimeMillis(), false);
}
+ @Nullable
+ protected RecentsWindowManager getRecentsWindowManager() {
+ return null;
+ }
+
@NonNull
protected abstract SWIPE_HANDLER createSwipeHandler(
long touchTimeMs, boolean continuingLastGesture);
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/FallbackSwipeHandlerTestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/FallbackSwipeHandlerTestCase.java
index 8d6906f..88197e5 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/FallbackSwipeHandlerTestCase.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/FallbackSwipeHandlerTestCase.java
@@ -16,6 +16,7 @@
package com.android.quickstep;
+import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import com.android.launcher3.util.LauncherMultivalentJUnit;
@@ -28,15 +29,14 @@
@SmallTest
@RunWith(LauncherMultivalentJUnit.class)
public class FallbackSwipeHandlerTestCase extends AbsSwipeUpHandlerTestCase<
- RecentsActivity,
RecentsState,
- FallbackRecentsView<RecentsActivity>,
RecentsActivity,
- FallbackActivityInterface,
- FallbackSwipeHandler> {
+ FallbackRecentsView<RecentsActivity>,
+ FallbackSwipeHandler,
+ FallbackActivityInterface> {
@Mock private RecentsActivity mRecentsActivity;
- @Mock private FallbackRecentsView mRecentsView;
+ @Mock private FallbackRecentsView<RecentsActivity> mRecentsView;
@Override
@@ -52,13 +52,15 @@
mInputConsumerController);
}
+ @NonNull
@Override
protected RecentsActivity getRecentsContainer() {
return mRecentsActivity;
}
+ @NonNull
@Override
- protected FallbackRecentsView getRecentsView() {
+ protected FallbackRecentsView<RecentsActivity> getRecentsView() {
return mRecentsView;
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2TestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2TestCase.java
index 653dc01..ec1dc8b 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2TestCase.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2TestCase.java
@@ -19,6 +19,7 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
+import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import com.android.launcher3.Hotseat;
@@ -38,12 +39,11 @@
@SmallTest
@RunWith(LauncherMultivalentJUnit.class)
public class LauncherSwipeHandlerV2TestCase extends AbsSwipeUpHandlerTestCase<
- QuickstepLauncher,
LauncherState,
- RecentsView<QuickstepLauncher, LauncherState>,
QuickstepLauncher,
- LauncherActivityInterface,
- LauncherSwipeHandlerV2> {
+ RecentsView<QuickstepLauncher, LauncherState>,
+ LauncherSwipeHandlerV2,
+ LauncherActivityInterface> {
@Mock private QuickstepLauncher mQuickstepLauncher;
@Mock private RecentsView<QuickstepLauncher, LauncherState> mRecentsView;
@@ -67,6 +67,7 @@
when(mWorkspace.getStateTransitionAnimation()).thenReturn(mTransitionAnimation);
}
+ @NonNull
@Override
protected LauncherSwipeHandlerV2 createSwipeHandler(
long touchTimeMs, boolean continuingLastGesture) {
@@ -80,11 +81,13 @@
mInputConsumerController);
}
+ @NonNull
@Override
protected QuickstepLauncher getRecentsContainer() {
return mQuickstepLauncher;
}
+ @NonNull
@Override
protected RecentsView<QuickstepLauncher, LauncherState> getRecentsView() {
return mRecentsView;
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsWindowSwipeHandlerTestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsWindowSwipeHandlerTestCase.java
new file mode 100644
index 0000000..1bdf273
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentsWindowSwipeHandlerTestCase.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.util.LauncherMultivalentJUnit;
+import com.android.quickstep.fallback.FallbackRecentsView;
+import com.android.quickstep.fallback.RecentsState;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
+import com.android.quickstep.fallback.window.RecentsWindowSwipeHandler;
+import com.android.quickstep.views.RecentsViewContainer;
+
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+@SmallTest
+@RunWith(LauncherMultivalentJUnit.class)
+public class RecentsWindowSwipeHandlerTestCase extends AbsSwipeUpHandlerTestCase<
+ RecentsState,
+ RecentsWindowManager,
+ FallbackRecentsView<RecentsWindowManager>,
+ RecentsWindowSwipeHandler,
+ FallbackWindowInterface> {
+
+ @Mock private RecentsWindowManager mRecentsWindowManager;
+ @Mock private FallbackRecentsView<RecentsWindowManager> mRecentsView;
+
+ @NonNull
+ @Override
+ protected RecentsWindowSwipeHandler createSwipeHandler(long touchTimeMs,
+ boolean continuingLastGesture) {
+ return new RecentsWindowSwipeHandler(
+ mContext,
+ mRecentsAnimationDeviceState,
+ mTaskAnimationManager,
+ mGestureState,
+ touchTimeMs,
+ continuingLastGesture,
+ mInputConsumerController,
+ mRecentsWindowManager);
+ }
+
+ @Nullable
+ @Override
+ protected RecentsWindowManager getRecentsWindowManager() {
+ return mRecentsWindowManager;
+ }
+
+ @NonNull
+ @Override
+ protected RecentsViewContainer getRecentsContainer() {
+ return mRecentsWindowManager;
+ }
+
+ @NonNull
+ @Override
+ protected FallbackRecentsView<RecentsWindowManager> getRecentsView() {
+ return mRecentsView;
+ }
+}
diff --git a/res/drawable/all_apps_tabs_background.xml b/res/drawable/all_apps_tabs_background.xml
index 62927af..d200b9f 100644
--- a/res/drawable/all_apps_tabs_background.xml
+++ b/res/drawable/all_apps_tabs_background.xml
@@ -13,36 +13,25 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/accent_ripple_color">
-
- <item android:id="@android:id/mask">
- <shape android:shape="rectangle">
- <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
- <solid android:color="@color/accent_ripple_color" />
- </shape>
- </item>
-
- <item>
- <selector android:enterFadeDuration="100">
- <item
- android:id="@+id/unselected"
- android:state_selected="false">
- <shape android:shape="rectangle">
- <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
- <solid android:color="?attr/materialColorSurfaceBright" />
- </shape>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/all_apps_tabs_background_unselected_focused" android:state_focused="true" android:state_selected="false" />
+ <item android:drawable="@drawable/all_apps_tabs_background_selected_focused" android:state_focused="true" android:state_selected="true" />
+ <item android:id="@+id/unselected" android:state_focused="false" android:state_selected="false">
+ <ripple android:color="@color/accent_ripple_color">
+ <item>
+ <selector android:enterFadeDuration="100">
+ <item android:drawable="@drawable/all_apps_tabs_background_unselected" />
+ </selector>
</item>
-
- <item
- android:id="@+id/selected"
- android:state_selected="true">
- <shape android:shape="rectangle">
- <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
- <solid android:color="?attr/materialColorPrimary" />
- </shape>
- </item>
- </selector>
+ </ripple>
</item>
-
-</ripple>
\ No newline at end of file
+ <item android:id="@+id/selected" android:state_focused="false" android:state_selected="true">
+ <ripple android:color="@color/accent_ripple_color">
+ <item>
+ <selector android:enterFadeDuration="100">
+ <item android:drawable="@drawable/all_apps_tabs_background_selected" />
+ </selector>
+ </item>
+ </ripple>
+ </item>
+</selector>
\ No newline at end of file
diff --git a/res/drawable/all_apps_tabs_background_selected.xml b/res/drawable/all_apps_tabs_background_selected.xml
new file mode 100644
index 0000000..47f95dd
--- /dev/null
+++ b/res/drawable/all_apps_tabs_background_selected.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:bottom="@dimen/all_apps_tabs_focus_width"
+ android:end="@dimen/all_apps_tabs_focus_width"
+ android:start="@dimen/all_apps_tabs_focus_width"
+ android:top="@dimen/all_apps_tabs_focus_width">
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
+ <solid android:color="?attr/materialColorPrimary" />
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/all_apps_tabs_background_selected_focused.xml b/res/drawable/all_apps_tabs_background_selected_focused.xml
new file mode 100644
index 0000000..e3d86c0
--- /dev/null
+++ b/res/drawable/all_apps_tabs_background_selected_focused.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <shape>
+ <corners android:radius="16dp" />
+ <solid android:color="?attr/materialColorPrimary" />
+ </shape>
+ </item>
+
+ <item
+ android:bottom="@dimen/all_apps_tabs_focus_border"
+ android:end="@dimen/all_apps_tabs_focus_border"
+ android:start="@dimen/all_apps_tabs_focus_border"
+ android:top="@dimen/all_apps_tabs_focus_border">
+ <shape android:shape="rectangle">
+ <corners android:radius="13dp" />
+ <solid android:color="?attr/materialColorPrimary" />
+ <stroke
+ android:width="@dimen/all_apps_tabs_focus_padding"
+ android:color="?attr/materialColorSurfaceDim" />
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/all_apps_tabs_background_unselected.xml b/res/drawable/all_apps_tabs_background_unselected.xml
new file mode 100644
index 0000000..ab592a8
--- /dev/null
+++ b/res/drawable/all_apps_tabs_background_unselected.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:bottom="@dimen/all_apps_tabs_focus_width"
+ android:end="@dimen/all_apps_tabs_focus_width"
+ android:start="@dimen/all_apps_tabs_focus_width"
+ android:top="@dimen/all_apps_tabs_focus_width">
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
+ <solid android:color="?attr/materialColorSurfaceBright" />
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/all_apps_tabs_background_unselected_focused.xml b/res/drawable/all_apps_tabs_background_unselected_focused.xml
new file mode 100644
index 0000000..0016102
--- /dev/null
+++ b/res/drawable/all_apps_tabs_background_unselected_focused.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <shape>
+ <corners android:radius="16dp" />
+ <solid android:color="?attr/materialColorPrimary" />
+ </shape>
+ </item>
+
+ <item
+ android:bottom="@dimen/all_apps_tabs_focus_border"
+ android:end="@dimen/all_apps_tabs_focus_border"
+ android:start="@dimen/all_apps_tabs_focus_border"
+ android:top="@dimen/all_apps_tabs_focus_border">
+ <shape android:shape="rectangle">
+ <corners android:radius="13dp" />
+ <solid android:color="?attr/materialColorSurfaceBright" />
+ <stroke
+ android:width="@dimen/all_apps_tabs_focus_padding"
+ android:color="?attr/materialColorSurfaceDim" />
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/layout/all_apps_personal_work_tabs.xml b/res/layout/all_apps_personal_work_tabs.xml
index e04b207..ecc5a14 100644
--- a/res/layout/all_apps_personal_work_tabs.xml
+++ b/res/layout/all_apps_personal_work_tabs.xml
@@ -21,8 +21,8 @@
android:layout_width="match_parent"
android:layout_height="@dimen/all_apps_header_pill_height"
android:layout_gravity="center_horizontal"
- android:paddingTop="@dimen/all_apps_tabs_vertical_padding"
- android:paddingBottom="@dimen/all_apps_tabs_vertical_padding"
+ android:paddingTop="@dimen/all_apps_tabs_vertical_padding_focus"
+ android:paddingBottom="@dimen/all_apps_tabs_vertical_padding_focus"
android:layout_marginTop="@dimen/all_apps_tabs_margin_top"
android:orientation="horizontal"
style="@style/TextHeadline"
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index f8c075f..d1e905d 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -126,6 +126,10 @@
<dimen name="all_apps_work_profile_tab_footer_bottom_padding">20dp</dimen>
<dimen name="all_apps_tabs_button_horizontal_padding">4dp</dimen>
<dimen name="all_apps_tabs_vertical_padding">6dp</dimen>
+ <dimen name="all_apps_tabs_vertical_padding_focus">1dp</dimen>
+ <dimen name="all_apps_tabs_focus_width">5dp</dimen>
+ <dimen name="all_apps_tabs_focus_border">3dp</dimen>
+ <dimen name="all_apps_tabs_focus_padding">2dp</dimen>
<dimen name="all_apps_tabs_margin_top">8dp</dimen>
<dimen name="all_apps_divider_height">2dp</dimen>
<dimen name="all_apps_divider_width">128dp</dimen>
diff --git a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
index 9f6b40b..d39c5de 100644
--- a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
+++ b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
@@ -20,6 +20,7 @@
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.util.DaggerSingletonTracker;
+import com.android.launcher3.util.ScreenOnTracker;
import dagger.BindsInstance;
@@ -34,6 +35,7 @@
public interface LauncherBaseAppComponent {
DaggerSingletonTracker getDaggerSingletonTracker();
InstallSessionHelper getInstallSessionHelper();
+ ScreenOnTracker getScreenOnTracker();
/** Builder for LauncherBaseAppComponent. */
interface Builder {
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index 27ec838..259e543 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -95,6 +95,7 @@
private static final int MESSAGE_ID_UPDATE_PREVIEW = 1337;
private static final int MESSAGE_ID_UPDATE_GRID = 7414;
+ private static final int MESSAGE_ID_UPDATE_COLOR = 856;
// Set of all active previews used to track duplicate memory allocations
private final Set<PreviewLifecycleObserver> mActivePreviews =
@@ -289,6 +290,11 @@
renderer.updateGrid(gridName);
}
break;
+ case MESSAGE_ID_UPDATE_COLOR:
+ if (Flags.newCustomizationPickerUi()) {
+ renderer.previewColor(message.getData());
+ }
+ break;
default:
// Unknown command, destroy lifecycle
Log.d(TAG, "Unknown preview command: " + message.what + ", destroying preview");
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 40c0cc6..f0e4fc4 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -98,6 +98,7 @@
import com.android.launcher3.widget.LauncherWidgetHolder;
import com.android.launcher3.widget.LocalColorExtractor;
import com.android.launcher3.widget.util.WidgetSizes;
+import com.android.systemui.shared.Flags;
import java.util.ArrayList;
import java.util.Collections;
@@ -150,6 +151,14 @@
InvariantDeviceProfile idp,
WallpaperColors wallpaperColorsOverride,
@Nullable final SparseArray<Size> launcherWidgetSpanInfo) {
+ this(context, idp, null, wallpaperColorsOverride, launcherWidgetSpanInfo);
+ }
+
+ public LauncherPreviewRenderer(Context context,
+ InvariantDeviceProfile idp,
+ SparseIntArray previewColorOverride,
+ WallpaperColors wallpaperColorsOverride,
+ @Nullable final SparseArray<Size> launcherWidgetSpanInfo) {
super(context);
mUiHandler = new Handler(Looper.getMainLooper());
@@ -206,12 +215,29 @@
mWorkspaceScreens.put(Workspace.SECOND_SCREEN_ID, rightPanel);
}
- WallpaperColors wallpaperColors = wallpaperColorsOverride != null
- ? wallpaperColorsOverride
- : WallpaperManager.getInstance(context).getWallpaperColors(FLAG_SYSTEM);
- mWallpaperColorResources = wallpaperColors != null
- ? LocalColorExtractor.newInstance(context).generateColorsOverride(wallpaperColors)
- : null;
+ if (Flags.newCustomizationPickerUi()) {
+ if (previewColorOverride != null) {
+ mWallpaperColorResources = previewColorOverride;
+ } else if (wallpaperColorsOverride != null) {
+ mWallpaperColorResources = LocalColorExtractor.newInstance(
+ context).generateColorsOverride(wallpaperColorsOverride);
+ } else {
+ WallpaperColors wallpaperColors = WallpaperManager.getInstance(
+ context).getWallpaperColors(FLAG_SYSTEM);
+ mWallpaperColorResources = wallpaperColors != null
+ ? LocalColorExtractor.newInstance(context).generateColorsOverride(
+ wallpaperColors)
+ : null;
+ }
+ } else {
+ WallpaperColors wallpaperColors = wallpaperColorsOverride != null
+ ? wallpaperColorsOverride
+ : WallpaperManager.getInstance(context).getWallpaperColors(FLAG_SYSTEM);
+ mWallpaperColorResources = wallpaperColors != null
+ ? LocalColorExtractor.newInstance(context).generateColorsOverride(
+ wallpaperColors)
+ : null;
+ }
mAppWidgetHost = new LauncherPreviewAppWidgetHost(context);
}
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 1b23d75..e3c2d36 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -32,6 +32,7 @@
import android.util.Log;
import android.util.Size;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.SurfaceControlViewHost;
@@ -81,6 +82,8 @@
private static final String KEY_VIEW_HEIGHT = "height";
private static final String KEY_DISPLAY_ID = "display_id";
private static final String KEY_COLORS = "wallpaper_colors";
+ private static final String KEY_COLOR_RESOURCE_IDS = "color_resource_ids";
+ private static final String KEY_COLOR_VALUES = "color_values";
private Context mContext;
private final IBinder mHostToken;
@@ -91,6 +94,7 @@
private final int mDisplayId;
private final Display mDisplay;
private final WallpaperColors mWallpaperColors;
+ private SparseIntArray mPreviewColorOverride;
private final RunnableList mLifeCycleTracker;
private final SurfaceControlViewHost mSurfaceControlViewHost;
@@ -110,6 +114,9 @@
mGridName = InvariantDeviceProfile.getCurrentGridName(context);
}
mWallpaperColors = bundle.getParcelable(KEY_COLORS);
+ if (Flags.newCustomizationPickerUi()) {
+ updateColorOverrides(bundle);
+ }
mHideQsb = bundle.getBoolean(GridCustomizationsProvider.KEY_HIDE_BOTTOM_ROW);
mHostToken = bundle.getBinder(KEY_HOST_TOKEN);
@@ -217,20 +224,60 @@
}
}
+ /**
+ * Updates the colors of the preview.
+ *
+ * @param bundle Bundle with an int array of color ids and an int array of overriding colors.
+ */
+ public void previewColor(Bundle bundle) {
+ updateColorOverrides(bundle);
+ loadAsync();
+ }
+
+ private void updateColorOverrides(Bundle bundle) {
+ int[] ids = bundle.getIntArray(KEY_COLOR_RESOURCE_IDS);
+ int[] colors = bundle.getIntArray(KEY_COLOR_VALUES);
+ if (ids != null && colors != null) {
+ mPreviewColorOverride = new SparseIntArray();
+ for (int i = 0; i < ids.length; i++) {
+ mPreviewColorOverride.put(ids[i], colors[i]);
+ }
+ } else {
+ mPreviewColorOverride = null;
+ }
+ }
+
/***
* Generates a new context overriding the theme color and the display size without affecting the
* main application context
*/
private Context getPreviewContext() {
Context context = mContext.createDisplayContext(mDisplay);
- if (mWallpaperColors == null) {
+ if (Flags.newCustomizationPickerUi()) {
+ if (mPreviewColorOverride != null) {
+ LocalColorExtractor.newInstance(context)
+ .applyColorsOverride(context, mPreviewColorOverride);
+ } else if (mWallpaperColors != null) {
+ LocalColorExtractor.newInstance(context)
+ .applyColorsOverride(context, mWallpaperColors);
+ }
+ if (mWallpaperColors != null) {
+ return new ContextThemeWrapper(context,
+ Themes.getActivityThemeRes(context, mWallpaperColors.getColorHints()));
+ } else {
+ return new ContextThemeWrapper(context,
+ Themes.getActivityThemeRes(context));
+ }
+ } else {
+ if (mWallpaperColors == null) {
+ return new ContextThemeWrapper(context,
+ Themes.getActivityThemeRes(context));
+ }
+ LocalColorExtractor.newInstance(context)
+ .applyColorsOverride(context, mWallpaperColors);
return new ContextThemeWrapper(context,
- Themes.getActivityThemeRes(context));
+ Themes.getActivityThemeRes(context, mWallpaperColors.getColorHints()));
}
- LocalColorExtractor.newInstance(context)
- .applyColorsOverride(context, mWallpaperColors);
- return new ContextThemeWrapper(context,
- Themes.getActivityThemeRes(context, mWallpaperColors.getColorHints()));
}
@WorkerThread
@@ -300,8 +347,13 @@
if (mDestroyed) {
return;
}
- mRenderer = new LauncherPreviewRenderer(inflationContext, idp,
- mWallpaperColors, launcherWidgetSpanInfo);
+ if (Flags.newCustomizationPickerUi()) {
+ mRenderer = new LauncherPreviewRenderer(inflationContext, idp, mPreviewColorOverride,
+ mWallpaperColors, launcherWidgetSpanInfo);
+ } else {
+ mRenderer = new LauncherPreviewRenderer(inflationContext, idp,
+ mWallpaperColors, launcherWidgetSpanInfo);
+ }
mRenderer.hideBottomRow(mHideQsb);
View view = mRenderer.getRenderedView(dataModel, widgetProviderInfoMap);
// This aspect scales the view to fit in the surface and centers it
diff --git a/src/com/android/launcher3/util/ScreenOnTracker.java b/src/com/android/launcher3/util/ScreenOnTracker.java
index 8ee799a..3582ad8 100644
--- a/src/com/android/launcher3/util/ScreenOnTracker.java
+++ b/src/com/android/launcher3/util/ScreenOnTracker.java
@@ -26,16 +26,22 @@
import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.dagger.ApplicationContext;
+import com.android.launcher3.dagger.LauncherAppSingleton;
+import com.android.launcher3.dagger.LauncherBaseAppComponent;
+
import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.function.Consumer;
+
+import javax.inject.Inject;
/**
* Utility class for tracking if the screen is currently on or off
*/
+@LauncherAppSingleton
public class ScreenOnTracker implements SafeCloseable {
- public static final MainThreadInitializedObject<ScreenOnTracker> INSTANCE =
- new MainThreadInitializedObject<>(ScreenOnTracker::new);
+ public static final DaggerSingletonObject<ScreenOnTracker> INSTANCE =
+ new DaggerSingletonObject<>(LauncherBaseAppComponent::getScreenOnTracker);
private final SimpleBroadcastReceiver mReceiver;
private final CopyOnWriteArrayList<ScreenOnListener> mListeners = new CopyOnWriteArrayList<>();
@@ -43,23 +49,26 @@
private final Context mContext;
private boolean mIsScreenOn;
- private ScreenOnTracker(Context context) {
+ @Inject
+ ScreenOnTracker(@ApplicationContext Context context, DaggerSingletonTracker tracker) {
// Assume that the screen is on to begin with
mContext = context;
mReceiver = new SimpleBroadcastReceiver(UI_HELPER_EXECUTOR, this::onReceive);
- init();
+ init(tracker);
}
@VisibleForTesting
- ScreenOnTracker(Context context, SimpleBroadcastReceiver receiver) {
+ ScreenOnTracker(@ApplicationContext Context context, SimpleBroadcastReceiver receiver,
+ DaggerSingletonTracker tracker) {
mContext = context;
mReceiver = receiver;
- init();
+ init(tracker);
}
- private void init() {
+ private void init(DaggerSingletonTracker tracker) {
mIsScreenOn = true;
mReceiver.register(mContext, ACTION_SCREEN_ON, ACTION_SCREEN_OFF, ACTION_USER_PRESENT);
+ ExecutorUtil.executeSyncOnMainOrFail(() -> tracker.addCloseable(this));
}
@Override
diff --git a/src/com/android/launcher3/widget/LocalColorExtractor.java b/src/com/android/launcher3/widget/LocalColorExtractor.java
index 7b500c7..d26eb38 100644
--- a/src/com/android/launcher3/widget/LocalColorExtractor.java
+++ b/src/com/android/launcher3/widget/LocalColorExtractor.java
@@ -48,4 +48,9 @@
public SparseIntArray generateColorsOverride(WallpaperColors colors) {
return null;
}
+
+ /**
+ * Updates the base context to contain the colors override
+ */
+ public void applyColorsOverride(Context base, SparseIntArray override) { }
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/ScreenOnTrackerTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/ScreenOnTrackerTest.kt
index 430aad2..45cc19c 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/ScreenOnTrackerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/ScreenOnTrackerTest.kt
@@ -39,13 +39,14 @@
@Mock private lateinit var receiver: SimpleBroadcastReceiver
@Mock private lateinit var context: Context
@Mock private lateinit var listener: ScreenOnTracker.ScreenOnListener
+ @Mock private lateinit var tracker: DaggerSingletonTracker
private lateinit var underTest: ScreenOnTracker
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- underTest = ScreenOnTracker(context, receiver)
+ underTest = ScreenOnTracker(context, receiver, tracker)
}
@Test