Merge "Improve taskId logging for Overview" into main
diff --git a/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java
deleted file mode 100644
index 3635827..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.taskbar;
-
-import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_NOTIFICATIONS;
-import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_QUICK_SETTINGS;
-
-import android.content.Context;
-import android.content.pm.ActivityInfo.Config;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.R;
-import com.android.launcher3.taskbar.navbutton.NearestTouchFrame;
-
-/**
- * Controller for managing buttons and status icons in taskbar in a desktop environment.
- */
-public class DesktopNavbarButtonsViewController extends NavbarButtonsViewController {
-
- private final TaskbarActivityContext mContext;
- private final FrameLayout mNavButtonsView;
- private final ViewGroup mNavButtonContainer;
- private final ViewGroup mStartContextualContainer;
- private final View mAllAppsButton;
-
- private TaskbarControllers mControllers;
-
- public DesktopNavbarButtonsViewController(TaskbarActivityContext context,
- @Nullable Context navigationBarPanelContext, NearestTouchFrame navButtonsView) {
- super(context, navigationBarPanelContext, navButtonsView);
- mContext = context;
- mNavButtonsView = navButtonsView;
- mNavButtonContainer = mNavButtonsView.findViewById(R.id.end_nav_buttons);
- mStartContextualContainer = mNavButtonsView.findViewById(R.id.start_contextual_buttons);
- mAllAppsButton = LayoutInflater.from(context)
- .inflate(R.layout.taskbar_all_apps_button, mStartContextualContainer, false);
- mAllAppsButton.setOnClickListener(v -> mControllers.taskbarAllAppsController.toggle());
- }
-
- /**
- * Initializes the controller
- */
- @Override
- public void init(TaskbarControllers controllers) {
- mControllers = controllers;
- super.init(controllers);
- }
-
- @Override
- protected void setupController() {
- mNavButtonsView.getLayoutParams().height = mContext.getDeviceProfile().taskbarHeight;
-
- // Quick settings and notifications buttons
- addButton(R.drawable.ic_sysbar_quick_settings, BUTTON_QUICK_SETTINGS,
- mNavButtonContainer, mControllers.navButtonController,
- R.id.quick_settings_button);
- addButton(R.drawable.ic_sysbar_notifications, BUTTON_NOTIFICATIONS,
- mNavButtonContainer, mControllers.navButtonController,
- R.id.notifications_button);
- // All apps button
- mStartContextualContainer.addView(mAllAppsButton);
- }
-
- /** Cleans up on destroy */
- @Override
- public void onDestroy() { }
-
- @Override
- public void onConfigurationChanged(@Config int configChanges) { }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRecentAppsController.java b/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRecentAppsController.java
deleted file mode 100644
index acfbea3..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRecentAppsController.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.taskbar;
-
-import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.util.SparseArray;
-
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.quickstep.RecentsModel;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Provides recent apps functionality specifically in a desktop environment.
- */
-public class DesktopTaskbarRecentAppsController extends TaskbarRecentAppsController {
-
- private final TaskbarActivityContext mContext;
- private ArrayList<ItemInfo> mRunningApps = new ArrayList<>();
- private AppInfo[] mApps;
-
- public DesktopTaskbarRecentAppsController(TaskbarActivityContext context) {
- mContext = context;
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mApps = null;
- }
-
- @Override
- protected void setApps(AppInfo[] apps) {
- mApps = apps;
- }
-
- @Override
- protected boolean isEnabled() {
- return true;
- }
-
- /**
- * Set mRunningApps to hold currently running applications using the list of currently running
- * tasks. Filtering is also done to ignore applications that are already on the taskbar in the
- * original hotseat.
- */
- @Override
- protected void updateRunningApps(SparseArray<ItemInfo> hotseatItems) {
- ArrayList<AppInfo> runningApps = getRunningAppsFromTasks();
- ArrayList<ItemInfo> filteredRunningApps = new ArrayList<>();
- for (AppInfo runningApp : runningApps) {
- boolean shouldAddOnTaskbar = true;
- for (int i = 0; i < hotseatItems.size(); i++) {
- if (hotseatItems.keyAt(i) >= mControllers.taskbarActivityContext.getDeviceProfile()
- .numShownHotseatIcons) {
- break;
- }
- if (hotseatItems.valueAt(i).getTargetPackage()
- .equals(runningApp.getTargetPackage())) {
- shouldAddOnTaskbar = false;
- break;
- }
- }
- if (shouldAddOnTaskbar) {
- filteredRunningApps.add(new WorkspaceItemInfo(runningApp));
- }
- }
- mRunningApps = filteredRunningApps;
- mControllers.taskbarViewController.commitRunningAppsToUI();
- }
-
- /**
- * Returns a copy of hotseatItems with the addition of currently running applications.
- */
- @Override
- protected ItemInfo[] updateHotseatItemInfos(ItemInfo[] hotseatItemInfos) {
- // hotseatItemInfos.length would be 0 if deviceProfile.numShownHotseatIcons is 0, so we
- // don't want to show anything in the hotseat
- if (hotseatItemInfos.length == 0) return hotseatItemInfos;
-
- int runningAppsIndex = 0;
- ItemInfo[] newHotseatItemsInfo = Arrays.copyOf(
- hotseatItemInfos, hotseatItemInfos.length + mRunningApps.size());
- for (int i = hotseatItemInfos.length; i < newHotseatItemsInfo.length; i++) {
- newHotseatItemsInfo[i] = mRunningApps.get(runningAppsIndex);
- runningAppsIndex++;
- }
- return newHotseatItemsInfo;
- }
-
-
- /**
- * Returns a list of running applications from the list of currently running tasks.
- */
- private ArrayList<AppInfo> getRunningAppsFromTasks() {
- ArrayList<ActivityManager.RunningTaskInfo> tasks =
- RecentsModel.INSTANCE.get(mContext).getRunningTasks();
- ArrayList<AppInfo> runningApps = new ArrayList<>();
- // early return if apps is empty, since we would have no AppInfo to compare
- if (mApps == null) {
- return runningApps;
- }
-
- Set<String> seenPackages = new HashSet<>();
- for (ActivityManager.RunningTaskInfo taskInfo : tasks) {
- if (taskInfo.realActivity == null) continue;
-
- // If a different task for the same package has already been handled, skip this one
- String taskPackage = taskInfo.realActivity.getPackageName();
- if (seenPackages.contains(taskPackage)) continue;
-
- // Otherwise, get the corresponding AppInfo and add it to the list
- seenPackages.add(taskPackage);
- AppInfo app = getAppInfo(taskInfo.realActivity);
- if (app == null) continue;
- runningApps.add(app);
- }
- return runningApps;
- }
-
- /**
- * Retrieves the corresponding AppInfo for the activity.
- */
- private AppInfo getAppInfo(ComponentName activity) {
- String packageName = activity.getPackageName();
- for (AppInfo app : mApps) {
- if (!packageName.equals(app.getTargetPackage())) {
- continue;
- }
- return app;
- }
- return null;
- }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsController.kt b/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsController.kt
index 06d25a2..3649c4e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsController.kt
@@ -40,12 +40,16 @@
*/
class DesktopTaskbarRunningAppsController(
private val recentsModel: RecentsModel,
- private val desktopVisibilityController: DesktopVisibilityController?,
+ // Pass a provider here instead of the actual DesktopVisibilityController instance since that
+ // instance might not be available when this constructor is called.
+ private val desktopVisibilityControllerProvider: () -> DesktopVisibilityController?,
) : TaskbarRecentAppsController() {
private var apps: Array<AppInfo>? = null
private var allRunningDesktopAppInfos: List<AppInfo>? = null
- private var runningDesktopAppInfosExceptHotseatItems: List<ItemInfo>? = null
+
+ private val desktopVisibilityController: DesktopVisibilityController?
+ get() = desktopVisibilityControllerProvider()
private val isInDesktopMode: Boolean
get() = desktopVisibilityController?.areDesktopTasksVisible() ?: false
@@ -63,20 +67,24 @@
override fun isEnabled() = true
@VisibleForTesting
- public override fun updateHotseatItemInfos(hotseatItems: Array<ItemInfo>?): Array<ItemInfo>? {
- val actualHotseatItems = hotseatItems ?: return super.updateHotseatItemInfos(null)
+ public override fun updateHotseatItemInfos(hotseatItems: Array<ItemInfo?>): Array<ItemInfo?> {
if (!isInDesktopMode) {
Log.d(TAG, "updateHotseatItemInfos: not in Desktop Mode")
return hotseatItems
}
val newHotseatItemInfos =
- actualHotseatItems
+ hotseatItems
+ .filterNotNull()
// Ignore predicted apps - we show running apps instead
.filter { itemInfo -> !itemInfo.isPredictedItem }
.toMutableList()
val runningDesktopAppInfos =
- runningDesktopAppInfosExceptHotseatItems ?: return newHotseatItemInfos.toTypedArray()
- newHotseatItemInfos.addAll(runningDesktopAppInfos)
+ allRunningDesktopAppInfos?.let {
+ getRunningDesktopAppInfosExceptHotseatApps(it, newHotseatItemInfos.toList())
+ }
+ if (runningDesktopAppInfos != null) {
+ newHotseatItemInfos.addAll(runningDesktopAppInfos)
+ }
return newHotseatItemInfos.toTypedArray()
}
@@ -88,19 +96,13 @@
}
@VisibleForTesting
- public override fun updateRunningApps(hotseatItems: SparseArray<ItemInfo>?) {
+ public override fun updateRunningApps() {
if (!isInDesktopMode) {
Log.d(TAG, "updateRunningApps: not in Desktop Mode")
mControllers.taskbarViewController.commitRunningAppsToUI()
return
}
- val allRunningDesktopAppInfos = getRunningDesktopAppInfos()
- this.allRunningDesktopAppInfos = allRunningDesktopAppInfos
- runningDesktopAppInfosExceptHotseatItems =
- hotseatItems?.let {
- getRunningDesktopAppInfosExceptHotseatApps(allRunningDesktopAppInfos, it.toList())
- }
-
+ allRunningDesktopAppInfos = getRunningDesktopAppInfos()
mControllers.taskbarViewController.commitRunningAppsToUI()
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarUIController.java
deleted file mode 100644
index 2dd610c4..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarUIController.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.taskbar;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.quickstep.util.TISBindHelper;
-
-/**
- * A data source which integrates with a Launcher instance, used specifically for a
- * desktop environment.
- */
-public class DesktopTaskbarUIController extends TaskbarUIController {
-
- private final QuickstepLauncher mLauncher;
-
- public DesktopTaskbarUIController(QuickstepLauncher launcher) {
- mLauncher = launcher;
- }
-
- @SuppressWarnings("MissingSuperCall") // TODO: Fix me
- @Override
- protected void init(TaskbarControllers taskbarControllers) {
- super.init(taskbarControllers);
- mLauncher.getHotseat().setIconsAlpha(0f);
- mControllers.taskbarViewController.updateRunningApps();
- }
-
- @SuppressWarnings("MissingSuperCall") // TODO: Fix me
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mLauncher.getHotseat().setIconsAlpha(1f);
- }
-
- /** Disable taskbar stashing in desktop environment. */
- @Override
- public boolean supportsVisualStashing() {
- return false;
- }
-
- @Nullable
- @Override
- protected TISBindHelper getTISBindHelper() {
- return mLauncher.getTISBindHelper();
- }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 1a94424..1d772b5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.taskbar;
-import static android.content.pm.PackageManager.FEATURE_PC;
import static android.os.Trace.TRACE_TAG_APP;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
@@ -248,8 +247,6 @@
mAccessibilityDelegate = new TaskbarShortcutMenuAccessibilityDelegate(this);
- final boolean isPcMode = getPackageManager().hasSystemFeature(FEATURE_PC);
-
// If Bubble bar is present, TaskbarControllers depends on it so build it first.
Optional<BubbleControllers> bubbleControllersOptional = Optional.empty();
BubbleBarController.onTaskbarRecreated();
@@ -280,11 +277,7 @@
mControllers = new TaskbarControllers(this,
new TaskbarDragController(this),
buttonController,
- isPcMode
- ? new DesktopNavbarButtonsViewController(this, mNavigationBarPanelContext,
- navButtonsView)
- : new NavbarButtonsViewController(this, mNavigationBarPanelContext,
- navButtonsView),
+ new NavbarButtonsViewController(this, mNavigationBarPanelContext, navButtonsView),
rotationButtonController,
new TaskbarDragLayerController(this, mDragLayer),
new TaskbarViewController(this, taskbarView),
@@ -305,7 +298,7 @@
new VoiceInteractionWindowController(this),
new TaskbarTranslationController(this),
new TaskbarSpringOnStashController(this),
- createTaskbarRecentAppsController(isPcMode),
+ createTaskbarRecentAppsController(),
TaskbarEduTooltipController.newInstance(this),
new KeyboardQuickSwitchController(),
new TaskbarPinningController(this),
@@ -314,16 +307,14 @@
mLauncherPrefs = LauncherPrefs.get(this);
}
- private TaskbarRecentAppsController createTaskbarRecentAppsController(boolean isPcMode) {
- if (isPcMode) return new DesktopTaskbarRecentAppsController(this);
+ private TaskbarRecentAppsController createTaskbarRecentAppsController() {
// TODO(b/335401172): unify DesktopMode checks in Launcher
- final boolean showRunningAppsInDesktopMode = enableDesktopWindowingMode()
- && enableDesktopWindowingTaskbarRunningApps();
- return showRunningAppsInDesktopMode
- ? new DesktopTaskbarRunningAppsController(
- RecentsModel.INSTANCE.get(this),
- LauncherActivityInterface.INSTANCE.getDesktopVisibilityController())
- : TaskbarRecentAppsController.DEFAULT;
+ if (enableDesktopWindowingMode() && enableDesktopWindowingTaskbarRunningApps()) {
+ return new DesktopTaskbarRunningAppsController(
+ RecentsModel.INSTANCE.get(this),
+ LauncherActivityInterface.INSTANCE::getDesktopVisibilityController);
+ }
+ return TaskbarRecentAppsController.DEFAULT;
}
/** Updates {@link DeviceProfile} instances for any Taskbar windows. */
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 95c4e25..4a8ed87 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -155,13 +155,16 @@
)
// if there's an animating bubble add it to the touch region so that it's clickable
- val animatingBubbleBounds =
+ val isAnimatingNewBubble =
controllers.bubbleControllers
.getOrNull()
?.bubbleBarViewController
- ?.animatingBubbleBounds
- if (animatingBubbleBounds != null) {
- defaultTouchableRegion.op(animatingBubbleBounds, Region.Op.UNION)
+ ?.isAnimatingNewBubble
+ ?: false
+ if (isAnimatingNewBubble) {
+ val iconBounds =
+ controllers.bubbleControllers.get().bubbleBarViewController.bubbleBarBounds
+ defaultTouchableRegion.op(iconBounds, Region.Op.UNION)
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 3196bfb..30954ed 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -16,7 +16,6 @@
package com.android.launcher3.taskbar;
import static android.content.Context.RECEIVER_NOT_EXPORTED;
-import static android.content.pm.PackageManager.FEATURE_PC;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
@@ -426,9 +425,6 @@
*/
private TaskbarUIController createTaskbarUIControllerForActivity(StatefulActivity activity) {
if (activity instanceof QuickstepLauncher) {
- if (mTaskbarActivityContext.getPackageManager().hasSystemFeature(FEATURE_PC)) {
- return new DesktopTaskbarUIController((QuickstepLauncher) activity);
- }
return new LauncherTaskbarUIController((QuickstepLauncher) activity);
}
if (activity instanceof RecentsActivity) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
index be87cfd..9f24d38 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
@@ -279,7 +279,7 @@
/** Call TaskbarRecentAppsController to update running apps with mHotseatItems. */
public void updateRunningApps() {
- mControllers.taskbarRecentAppsController.updateRunningApps(mHotseatItems);
+ mControllers.taskbarRecentAppsController.updateRunningApps();
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java
index 9b84f1b..a29c74b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java
@@ -17,9 +17,8 @@
import static java.util.Collections.emptySet;
-import android.util.SparseArray;
-
import androidx.annotation.CallSuper;
+import androidx.annotation.NonNull;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -59,13 +58,12 @@
}
/** Called to update hotseatItems, no-op except in desktop environment. */
- protected ItemInfo[] updateHotseatItemInfos(ItemInfo[] hotseatItems) {
+ protected ItemInfo[] updateHotseatItemInfos(@NonNull ItemInfo[] hotseatItems) {
return hotseatItems;
}
/** Called to update the list of currently running apps, no-op except in desktop environment. */
- protected void updateRunningApps(SparseArray<ItemInfo> hotseatItems) {
- }
+ protected void updateRunningApps() {}
/** Returns the currently running apps, or an empty Set if outside of Desktop environment. */
public Set<String> getRunningApps() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 2162a73..182ff7e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -319,8 +319,7 @@
updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, isStashedInAppAuto);
updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, isInSetup);
updateStateForFlag(FLAG_IN_SETUP, isInSetup);
- updateStateForFlag(FLAG_STASHED_SMALL_SCREEN, mActivity.isPhoneMode()
- && !mActivity.isThreeButtonNav());
+ updateStateForFlag(FLAG_STASHED_SMALL_SCREEN, mActivity.isPhoneGestureNavMode());
// For now, assume we're in an app, since LauncherTaskbarUIController won't be able to tell
// us that we're paused until a bit later. This avoids flickering upon recreating taskbar.
updateStateForFlag(FLAG_IN_APP, true);
@@ -356,6 +355,7 @@
boolean hideTaskbar = isVisible || !mActivity.isUserSetupComplete();
updateStateForFlag(FLAG_IN_SETUP, hideTaskbar);
updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, hideTaskbar);
+ updateStateForFlag(FLAG_STASHED_SMALL_SCREEN, mActivity.isPhoneGestureNavMode());
applyState(hideTaskbar ? 0 : getStashDuration());
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index effef3c..77f8a8a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.taskbar;
-import static android.content.pm.PackageManager.FEATURE_PC;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
import static com.android.launcher3.BubbleTextView.DISPLAY_TASKBAR;
@@ -157,23 +156,21 @@
// Needed to draw folder leave-behind when opening one.
setWillNotDraw(false);
- if (!mActivityContext.getPackageManager().hasSystemFeature(FEATURE_PC)) {
- mAllAppsButton = (IconButtonView) LayoutInflater.from(context)
- .inflate(R.layout.taskbar_all_apps_button, this, false);
- mAllAppsButton.setIconDrawable(resources.getDrawable(
- getAllAppsButton(isTransientTaskbar)));
- mAllAppsButton.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
- mAllAppsButton.setForegroundTint(
- mActivityContext.getColor(R.color.all_apps_button_color));
+ mAllAppsButton = (IconButtonView) LayoutInflater.from(context)
+ .inflate(R.layout.taskbar_all_apps_button, this, false);
+ mAllAppsButton.setIconDrawable(resources.getDrawable(
+ getAllAppsButton(isTransientTaskbar)));
+ mAllAppsButton.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
+ mAllAppsButton.setForegroundTint(
+ mActivityContext.getColor(R.color.all_apps_button_color));
- if (enableTaskbarPinning()) {
- mTaskbarDivider = (IconButtonView) LayoutInflater.from(context).inflate(
- R.layout.taskbar_divider,
- this, false);
- mTaskbarDivider.setIconDrawable(
- resources.getDrawable(R.drawable.taskbar_divider_button));
- mTaskbarDivider.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
- }
+ if (enableTaskbarPinning()) {
+ mTaskbarDivider = (IconButtonView) LayoutInflater.from(context).inflate(
+ R.layout.taskbar_divider,
+ this, false);
+ mTaskbarDivider.setIconDrawable(
+ resources.getDrawable(R.drawable.taskbar_divider_button));
+ mTaskbarDivider.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
}
// TODO: Disable touch events on QSB otherwise it can crash.
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 981c9f9..66e5302 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -419,9 +419,7 @@
}
if (update.bubbleBarLocation != null) {
if (update.bubbleBarLocation != mBubbleBarViewController.getBubbleBarLocation()) {
- // Animate when receiving updates. Skip it if we received the initial state.
- boolean animate = !update.initialState;
- updateBubbleBarLocationInternal(update.bubbleBarLocation, animate);
+ updateBubbleBarLocationInternal(update.bubbleBarLocation);
}
}
}
@@ -483,15 +481,21 @@
* Updates the value locally in Launcher and in WMShell.
*/
public void updateBubbleBarLocation(BubbleBarLocation location) {
- updateBubbleBarLocationInternal(location, false /* animate */);
+ updateBubbleBarLocationInternal(location);
mSystemUiProxy.setBubbleBarLocation(location);
}
- private void updateBubbleBarLocationInternal(BubbleBarLocation location, boolean animate) {
- mBubbleBarViewController.setBubbleBarLocation(location, animate);
+ private void updateBubbleBarLocationInternal(BubbleBarLocation location) {
+ mBubbleBarViewController.setBubbleBarLocation(location);
mBubbleStashController.setBubbleBarLocation(location);
}
+ @Override
+ public void animateBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
+ mMainExecutor.execute(
+ () -> mBubbleBarViewController.animateBubbleBarLocation(bubbleBarLocation));
+ }
+
//
// Loading data for the bubbles
//
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index 5234936..60e8abe 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -104,8 +104,6 @@
* updates the bounds and accounts for translation.
*/
private final Rect mBubbleBarBounds = new Rect();
- /** The bounds of the animating bubble in the coordinate space of the BubbleBarView. */
- private final Rect mAnimatingBubbleBounds = new Rect();
// The amount the bubbles overlap when they are stacked in the bubble bar
private final float mIconOverlapAmount;
// The spacing between the bubbles when bubble bar is expanded
@@ -155,8 +153,6 @@
private int mPreviousLayoutDirection = LayoutDirection.UNDEFINED;
- private boolean mLocationChangePending;
-
public BubbleBarView(Context context) {
this(context, null);
}
@@ -185,12 +181,12 @@
setClipToPadding(false);
- mBubbleBarBackground = new BubbleBarBackground(context, getBubbleBarHeight());
+ mBubbleBarBackground = new BubbleBarBackground(context, getBubbleBarExpandedHeight());
setBackgroundDrawable(mBubbleBarBackground);
mWidthAnimator.setDuration(WIDTH_ANIMATION_DURATION_MS);
mWidthAnimator.addUpdateListener(animation -> {
- updateChildrenRenderNodeProperties();
+ updateChildrenRenderNodeProperties(mBubbleBarLocation);
invalidate();
});
mWidthAnimator.addListener(new Animator.AnimatorListener() {
@@ -246,7 +242,7 @@
params.width = (int) mIconSize;
childView.setLayoutParams(params);
}
- mBubbleBarBackground.setHeight(getBubbleBarHeight());
+ mBubbleBarBackground.setHeight(getBubbleBarExpandedHeight());
updateLayoutParams();
}
@@ -264,7 +260,7 @@
setPivotY(mRelativePivotY * getHeight());
// Position the views
- updateChildrenRenderNodeProperties();
+ updateChildrenRenderNodeProperties(mBubbleBarLocation);
}
@Override
@@ -280,7 +276,6 @@
@SuppressLint("RtlHardcoded")
private void onBubbleBarLocationChanged() {
- mLocationChangePending = false;
final boolean onLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl());
mBubbleBarBackground.setAnchorLeft(onLeft);
mRelativePivotX = onLeft ? 0f : 1f;
@@ -301,11 +296,18 @@
/**
* Update {@link BubbleBarLocation}
*/
- public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation, boolean animate) {
- if (animate) {
- animateToBubbleBarLocation(bubbleBarLocation);
- } else {
- setBubbleBarLocationInternal(bubbleBarLocation);
+ public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
+ if (mBubbleBarLocationAnimator != null) {
+ mBubbleBarLocationAnimator.removeAllListeners();
+ mBubbleBarLocationAnimator.cancel();
+ mBubbleBarLocationAnimator = null;
+ }
+ setTranslationX(0f);
+ setAlpha(1f);
+ if (bubbleBarLocation != mBubbleBarLocation) {
+ mBubbleBarLocation = bubbleBarLocation;
+ onBubbleBarLocationChanged();
+ invalidate();
}
}
@@ -318,51 +320,37 @@
}
mDragging = dragging;
setElevation(dragging ? mDragElevation : mBubbleElevation);
- if (!dragging && mLocationChangePending) {
- // During drag finish animation we may update the translation x value to shift the
- // bubble to the new drop target. Clear the translation here.
- setTranslationX(0f);
- onBubbleBarLocationChanged();
- }
}
/**
- * Adjust resting position for the bubble bar while it is being dragged.
- * <p>
- * Bubble bar is laid out on left or right side of the screen. When it is being dragged to
- * the opposite side, the resting position should be on that side. Calculate any additional
- * translation that may be required to move the bubble bar to the new side.
+ * Get translation for bubble bar when drag is released and it needs to animate back to the
+ * resting position.
+ * Resting position is based on the supplied location. If the supplied location is different
+ * from the internal location that was used to lay out the bubble bar, translation values are
+ * calculated to position the bar at the desired location.
*
- * @param restingPosition relative resting position of the bubble bar from the laid out position
+ * @param initialTranslation initial bubble bar translation at the start of drag
+ * @param location desired location of the bubble bar when drag is released
+ * @return point with x and y values representing translation on x and y-axis
*/
- @SuppressLint("RtlHardcoded")
- void adjustRelativeRestingPosition(PointF restingPosition) {
- final boolean locationOnLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl());
- // Bubble bar is placed left or right with gravity. Check where it is currently.
- final int absoluteGravity = Gravity.getAbsoluteGravity(
- ((LayoutParams) getLayoutParams()).gravity, getLayoutDirection());
- final boolean gravityOnLeft =
- (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.LEFT;
-
- // Bubble bar is pinned to the same side per gravity and the desired location.
- // Resting translation does not need to be adjusted.
- if (locationOnLeft == gravityOnLeft) {
- return;
- }
-
+ public PointF getBubbleBarDragReleaseTranslation(PointF initialTranslation,
+ BubbleBarLocation location) {
+ // Start with the initial translation. Value on y-axis can be reused.
+ final PointF dragEndTranslation = new PointF(initialTranslation);
// Bubble bar is laid out on left or right side of the screen. And the desired new
- // location is on the other side. Calculate x translation value required to shift the
+ // location is on the other side. Calculate x translation value required to shift
// bubble bar from one side to the other.
- float x = getDistanceFromOtherSide();
- if (locationOnLeft) {
+ final float shift = getDistanceFromOtherSide();
+ if (location.isOnLeft(isLayoutRtl())) {
// New location is on the left, shift left
// before -> |......ooo.| after -> |.ooo......|
- restingPosition.x = -x;
+ dragEndTranslation.x = -shift;
} else {
// New location is on the right, shift right
// before -> |.ooo......| after -> |......ooo.|
- restingPosition.x = x;
+ dragEndTranslation.x = shift;
}
+ return dragEndTranslation;
}
private float getDistanceFromOtherSide() {
@@ -376,49 +364,40 @@
return (float) (displayWidth - getWidth() - margin);
}
- private void setBubbleBarLocationInternal(BubbleBarLocation bubbleBarLocation) {
- if (bubbleBarLocation != mBubbleBarLocation) {
- mBubbleBarLocation = bubbleBarLocation;
- if (mDragging) {
- mLocationChangePending = true;
- } else {
- onBubbleBarLocationChanged();
- invalidate();
- }
- }
- }
-
- private void animateToBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
- if (bubbleBarLocation == mBubbleBarLocation) {
- // nothing to do, already at expected location
- return;
- }
+ /**
+ * Animate bubble bar to the given location transiently. Does not modify the layout or the value
+ * returned by {@link #getBubbleBarLocation()}.
+ */
+ public void animateToBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
if (mBubbleBarLocationAnimator != null && mBubbleBarLocationAnimator.isRunning()) {
+ mBubbleBarLocationAnimator.removeAllListeners();
mBubbleBarLocationAnimator.cancel();
}
// Location animation uses two separate animators.
// First animator hides the bar.
- // After it completes, location update is sent to layout the bar in the new location.
+ // After it completes, bubble positions in the bar and arrow position is updated.
// Second animator is started to show the bar.
- mBubbleBarLocationAnimator = getLocationUpdateFadeOutAnimator();
+ mBubbleBarLocationAnimator = getLocationUpdateFadeOutAnimator(bubbleBarLocation);
mBubbleBarLocationAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- // Bubble bar is not visible, update the location
- setBubbleBarLocationInternal(bubbleBarLocation);
+ updateChildrenRenderNodeProperties(bubbleBarLocation);
+ mBubbleBarBackground.setAnchorLeft(bubbleBarLocation.isOnLeft(isLayoutRtl()));
+
// Animate it in
- mBubbleBarLocationAnimator = getLocationUpdateFadeInAnimator();
+ mBubbleBarLocationAnimator = getLocationUpdateFadeInAnimator(bubbleBarLocation);
mBubbleBarLocationAnimator.start();
}
});
mBubbleBarLocationAnimator.start();
}
- private AnimatorSet getLocationUpdateFadeOutAnimator() {
+ private Animator getLocationUpdateFadeOutAnimator(BubbleBarLocation bubbleBarLocation) {
final float shift =
getResources().getDisplayMetrics().widthPixels * FADE_OUT_ANIM_POSITION_SHIFT;
- final float tx = mBubbleBarLocation.isOnLeft(isLayoutRtl()) ? shift : -shift;
+ final boolean onLeft = bubbleBarLocation.isOnLeft(isLayoutRtl());
+ final float tx = getTranslationX() + (onLeft ? shift : -shift);
ObjectAnimator positionAnim = ObjectAnimator.ofFloat(this, TRANSLATION_X, tx)
.setDuration(FADE_OUT_ANIM_POSITION_DURATION_MS);
@@ -433,14 +412,31 @@
return animatorSet;
}
- private Animator getLocationUpdateFadeInAnimator() {
+ private Animator getLocationUpdateFadeInAnimator(BubbleBarLocation animatedLocation) {
final float shift =
getResources().getDisplayMetrics().widthPixels * FADE_IN_ANIM_POSITION_SHIFT;
- final float startTx = mBubbleBarLocation.isOnLeft(isLayoutRtl()) ? shift : -shift;
+
+ final boolean onLeft = animatedLocation.isOnLeft(isLayoutRtl());
+ final float startTx;
+ final float finalTx;
+ if (animatedLocation == mBubbleBarLocation) {
+ // Animated location matches layout location.
+ finalTx = 0;
+ } else {
+ // We are animating in to a transient location, need to move the bar accordingly.
+ finalTx = getDistanceFromOtherSide() * (onLeft ? -1 : 1);
+ }
+ if (onLeft) {
+ // Bar will be shown on the left side. Start point is shifted right.
+ startTx = finalTx + shift;
+ } else {
+ // Bar will be shown on the right side. Start point is shifted left.
+ startTx = finalTx - shift;
+ }
ValueAnimator positionAnim = new SpringAnimationBuilder(getContext())
.setStartValue(startTx)
- .setEndValue(0)
+ .setEndValue(finalTx)
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
.setStiffness(FADE_IN_ANIM_POSITION_SPRING_STIFFNESS)
.build(this, VIEW_TRANSLATE_X);
@@ -462,30 +458,6 @@
return mBubbleBarBounds;
}
- /** Returns the bounds of the animating bubble, or {@code null} if no bubble is animating. */
- @Nullable
- public Rect getAnimatingBubbleBounds() {
- if (mIsAnimatingNewBubble) {
- return mAnimatingBubbleBounds;
- }
- return null;
- }
-
- /**
- * Updates the animating bubble bounds. This should be called when the bubble is fully animated
- * in so that we can include it in taskbar touchable region.
- *
- * <p>The bounds are adjusted to the coordinate space of BubbleBarView so that it can be used
- * by taskbar.
- */
- public void updateAnimatingBubbleBounds(int left, int top, int width, int height) {
- Rect bubbleBarBounds = getBubbleBarBounds();
- mAnimatingBubbleBounds.left = bubbleBarBounds.left + left;
- mAnimatingBubbleBounds.top = bubbleBarBounds.top + top;
- mAnimatingBubbleBounds.right = mAnimatingBubbleBounds.left + width;
- mAnimatingBubbleBounds.bottom = mAnimatingBubbleBounds.top + height;
- }
-
/**
* Set bubble bar relative pivot value for X and Y, applied as a fraction of view width/height
* respectively. If the value is not in range of 0 to 1 it will be normalized.
@@ -498,6 +470,11 @@
requestLayout();
}
+ /** Like {@link #setRelativePivot(float, float)} but only updates pivot y. */
+ public void setRelativePivotY(float y) {
+ setRelativePivot(mRelativePivotX, y);
+ }
+
/**
* Get current relative pivot for X axis
*/
@@ -512,38 +489,14 @@
return mRelativePivotY;
}
- /** Prepares for animating a bubble while being stashed. */
- public void prepareForAnimatingBubbleWhileStashed(String bubbleKey) {
+ /** Notifies the bubble bar that a new bubble animation is starting. */
+ public void onAnimatingBubbleStarted() {
mIsAnimatingNewBubble = true;
- // we're about to animate the new bubble in. the new bubble has already been added to this
- // view, but we're currently stashed, so before we can start the animation we need make
- // everything else in the bubble bar invisible, except for the bubble that's being animated.
- setBackground(null);
- for (int i = 0; i < getChildCount(); i++) {
- final BubbleView view = (BubbleView) getChildAt(i);
- final String key = view.getBubble().getKey();
- if (!bubbleKey.equals(key)) {
- view.setVisibility(INVISIBLE);
- }
- }
- setVisibility(VISIBLE);
- setAlpha(1);
- setTranslationY(0);
- setScaleX(1);
- setScaleY(1);
}
- /** Resets the state after the bubble animation completed. */
+ /** Notifies the bubble bar that a new bubble animation is complete. */
public void onAnimatingBubbleCompleted() {
mIsAnimatingNewBubble = false;
- // setting the background triggers relayout so no need to explicitly invalidate after the
- // animation
- setBackground(mBubbleBarBackground);
- for (int i = 0; i < getChildCount(); i++) {
- final BubbleView view = (BubbleView) getChildAt(i);
- view.setVisibility(VISIBLE);
- view.setAlpha(1f);
- }
}
// TODO: (b/280605790) animate it
@@ -577,7 +530,7 @@
private void updateLayoutParams() {
LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
- lp.height = getBubbleBarHeight();
+ lp.height = (int) getBubbleBarExpandedHeight();
lp.width = (int) (mIsBarExpanded ? expandedWidth() : collapsedWidth());
setLayoutParams(lp);
}
@@ -592,13 +545,7 @@
* Updates the z order, positions, and badge visibility of the bubble views in the bar based
* on the expanded state.
*/
- private void updateChildrenRenderNodeProperties() {
- if (mIsAnimatingNewBubble) {
- // don't update bubbles if a new bubble animation is playing.
- // the bubble bar will redraw itself via onLayout after the animation.
- return;
- }
-
+ private void updateChildrenRenderNodeProperties(BubbleBarLocation bubbleBarLocation) {
final float widthState = (float) mWidthAnimator.getAnimatedValue();
final float currentWidth = getWidth();
final float expandedWidth = expandedWidth();
@@ -606,7 +553,7 @@
int bubbleCount = getChildCount();
final float ty = (mBubbleBarBounds.height() - mIconSize) / 2f;
final boolean animate = getVisibility() == VISIBLE;
- final boolean onLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl());
+ final boolean onLeft = bubbleBarLocation.isOnLeft(isLayoutRtl());
// elevation state is opposite to widthState - when expanded all icons are flat
float elevationState = (1 - widthState);
for (int i = 0; i < bubbleCount; i++) {
@@ -664,8 +611,9 @@
}
// update the arrow position
- final float collapsedArrowPosition = arrowPositionForSelectedWhenCollapsed();
- final float expandedArrowPosition = arrowPositionForSelectedWhenExpanded();
+ final float collapsedArrowPosition = arrowPositionForSelectedWhenCollapsed(
+ bubbleBarLocation);
+ final float expandedArrowPosition = arrowPositionForSelectedWhenExpanded(bubbleBarLocation);
final float interpolatedWidth =
widthState * (expandedWidth - collapsedWidth) + collapsedWidth;
final float arrowPosition;
@@ -712,7 +660,7 @@
addViewInLayout(child, i, child.getLayoutParams());
}
}
- updateChildrenRenderNodeProperties();
+ updateChildrenRenderNodeProperties(mBubbleBarLocation);
}
}
@@ -753,7 +701,7 @@
return;
}
// Find the center of the bubble when it's expanded, set the arrow position to it.
- final float tx = arrowPositionForSelectedWhenExpanded();
+ final float tx = arrowPositionForSelectedWhenExpanded(mBubbleBarLocation);
final float currentArrowPosition = mBubbleBarBackground.getArrowPositionX();
if (tx == currentArrowPosition) {
// arrow position remains unchanged
@@ -778,10 +726,10 @@
}
}
- private float arrowPositionForSelectedWhenExpanded() {
+ private float arrowPositionForSelectedWhenExpanded(BubbleBarLocation bubbleBarLocation) {
final int index = indexOfChild(mSelectedBubbleView);
final int bubblePosition;
- if (mBubbleBarLocation.isOnLeft(isLayoutRtl())) {
+ if (bubbleBarLocation.isOnLeft(isLayoutRtl())) {
// Bubble positions are reversed. First bubble is on the right.
bubblePosition = getChildCount() - index - 1;
} else {
@@ -791,10 +739,10 @@
+ mIconSize / 2f;
}
- private float arrowPositionForSelectedWhenCollapsed() {
+ private float arrowPositionForSelectedWhenCollapsed(BubbleBarLocation bubbleBarLocation) {
final int index = indexOfChild(mSelectedBubbleView);
final int bubblePosition;
- if (mBubbleBarLocation.isOnLeft(isLayoutRtl())) {
+ if (bubbleBarLocation.isOnLeft(isLayoutRtl())) {
// Bubble positions are reversed. First bubble may be shifted, if there are more
// bubbles than the current bubble and overflow.
bubblePosition = index == 0 && getChildCount() > 2 ? 1 : 0;
@@ -864,8 +812,13 @@
: mIconSize + horizontalPadding;
}
- private int getBubbleBarHeight() {
- return (int) (mIconSize + mBubbleBarPadding * 2 + mPointerSize);
+ private float getBubbleBarExpandedHeight() {
+ return getBubbleBarCollapsedHeight() + mPointerSize;
+ }
+
+ float getBubbleBarCollapsedHeight() {
+ // the pointer is invisible when collapsed
+ return mIconSize + mBubbleBarPadding * 2;
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 3c46f32..dc48a66 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -189,6 +189,10 @@
return mBubbleBarTranslationY;
}
+ float getBubbleBarCollapsedHeight() {
+ return mBarView.getBubbleBarCollapsedHeight();
+ }
+
/**
* Whether the bubble bar is visible or not.
*/
@@ -211,8 +215,17 @@
/**
* Update bar {@link BubbleBarLocation}
*/
- public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation, boolean animate) {
- mBarView.setBubbleBarLocation(bubbleBarLocation, animate);
+ public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
+ mBarView.setBubbleBarLocation(bubbleBarLocation);
+ }
+
+ /**
+ * Animate bubble bar to the given location. The location change is transient. It does not
+ * update the state of the bubble bar.
+ * To update bubble bar pinned location, use {@link #setBubbleBarLocation(BubbleBarLocation)}.
+ */
+ public void animateBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
+ mBarView.animateToBubbleBarLocation(bubbleBarLocation);
}
/**
@@ -222,10 +235,9 @@
return mBarView.getBubbleBarBounds();
}
- /** The bounds of the animating bubble, or {@code null} if no bubble is animating. */
- @Nullable
- public Rect getAnimatingBubbleBounds() {
- return mBarView.getAnimatingBubbleBounds();
+ /** Whether a new bubble is animating. */
+ public boolean isAnimatingNewBubble() {
+ return mBarView.isAnimatingNewBubble();
}
/** The horizontal margin of the bubble bar from the edge of the screen. */
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
index 8b811d9..49f114a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
@@ -110,25 +110,25 @@
/**
* Animates the dragged bubble movement back to the initial position.
*
- * @param initialPosition the position to animate to
+ * @param restingPosition the position to animate to
* @param velocity the initial velocity to use for the spring animation
* @param endActions gets called when the animation completes or gets cancelled
*/
- public void animateToInitialState(@NonNull PointF initialPosition, @NonNull PointF velocity,
+ public void animateToRestingState(@NonNull PointF restingPosition, @NonNull PointF velocity,
@Nullable Runnable endActions) {
mBubbleAnimator.cancel();
mBubbleAnimator
.spring(DynamicAnimation.SCALE_X, 1f)
.spring(DynamicAnimation.SCALE_Y, 1f)
- .spring(DynamicAnimation.TRANSLATION_X, initialPosition.x, velocity.x,
+ .spring(DynamicAnimation.TRANSLATION_X, restingPosition.x, velocity.x,
mTranslationConfig)
- .spring(DynamicAnimation.TRANSLATION_Y, initialPosition.y, velocity.y,
+ .spring(DynamicAnimation.TRANSLATION_Y, restingPosition.y, velocity.y,
mTranslationConfig)
.addEndListener((View target, @NonNull FloatPropertyCompat<? super View> property,
boolean wasFling, boolean canceled, float finalValue, float finalVelocity,
boolean allRelevantPropertyAnimationsEnded) -> {
if (canceled || allRelevantPropertyAnimationsEnded) {
- resetAnimatedViews(initialPosition);
+ resetAnimatedViews(restingPosition);
if (endActions != null) {
endActions.run();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
index 5ffc6d8..d1c9da7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
@@ -26,6 +26,8 @@
import androidx.annotation.Nullable;
import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.wm.shell.common.bubbles.BaseBubblePinController.LocationChangeListener;
+import com.android.wm.shell.common.bubbles.BubbleBarLocation;
/**
* Controls bubble bar drag interactions.
@@ -37,6 +39,7 @@
*/
public class BubbleDragController {
private final TaskbarActivityContext mActivity;
+ private BubbleBarController mBubbleBarController;
private BubbleBarViewController mBubbleBarViewController;
private BubbleDismissController mBubbleDismissController;
private BubbleBarPinController mBubbleBarPinController;
@@ -51,11 +54,10 @@
* controllers may still be waiting for init().
*/
public void init(@NonNull BubbleControllers bubbleControllers) {
+ mBubbleBarController = bubbleControllers.bubbleBarController;
mBubbleBarViewController = bubbleControllers.bubbleBarViewController;
mBubbleDismissController = bubbleControllers.bubbleDismissController;
mBubbleBarPinController = bubbleControllers.bubbleBarPinController;
- mBubbleBarPinController.setListener(
- bubbleControllers.bubbleBarController::updateBubbleBarLocation);
mBubbleDismissController.setListener(
stuck -> mBubbleBarPinController.setDropTargetHidden(stuck));
}
@@ -96,6 +98,17 @@
PointF initialRelativePivot = new PointF();
bubbleBarView.setOnTouchListener(new BubbleTouchListener() {
+ @Nullable
+ private BubbleBarLocation mReleasedLocation;
+
+ private final LocationChangeListener mLocationChangeListener =
+ new LocationChangeListener() {
+ @Override
+ public void onRelease(@NonNull BubbleBarLocation location) {
+ mReleasedLocation = location;
+ }
+ };
+
@Override
protected boolean onTouchDown(@NonNull View view, @NonNull MotionEvent event) {
if (bubbleBarView.isExpanded()) return false;
@@ -104,6 +117,7 @@
@Override
void onDragStart() {
+ mBubbleBarPinController.setListener(mLocationChangeListener);
initialRelativePivot.set(bubbleBarView.getRelativePivotX(),
bubbleBarView.getRelativePivotY());
// By default the bubble bar view pivot is in bottom right corner, while dragging
@@ -134,13 +148,17 @@
// Restoring the initial pivot for the bubble bar view
bubbleBarView.setRelativePivot(initialRelativePivot.x, initialRelativePivot.y);
bubbleBarView.setIsDragging(false);
+ mBubbleBarController.updateBubbleBarLocation(mReleasedLocation);
}
@Override
protected PointF getRestingPosition() {
- PointF restingPosition = super.getRestingPosition();
- bubbleBarView.adjustRelativeRestingPosition(restingPosition);
- return restingPosition;
+ if (mReleasedLocation == null
+ || mReleasedLocation == bubbleBarView.getBubbleBarLocation()) {
+ return getInitialPosition();
+ }
+ return bubbleBarView.getBubbleBarDragReleaseTranslation(getInitialPosition(),
+ mReleasedLocation);
}
});
}
@@ -229,6 +247,13 @@
}
/**
+ * Get the initial position of the view when drag started
+ */
+ protected PointF getInitialPosition() {
+ return mViewInitialPosition;
+ }
+
+ /**
* Get the resting position of the view when drag is released
*/
protected PointF getRestingPosition() {
@@ -362,7 +387,7 @@
mAnimator.animateDismiss(mViewInitialPosition, onComplete);
} else {
onDragRelease();
- mAnimator.animateToInitialState(getRestingPosition(), getCurrentVelocity(),
+ mAnimator.animateToRestingState(getRestingPosition(), getCurrentVelocity(),
onComplete);
}
mBubbleDismissController.hideDismissView();
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
index bea0af8..f689a05 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
@@ -25,6 +25,7 @@
import android.view.MotionEvent;
import android.view.View;
+import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.taskbar.StashedHandleViewController;
import com.android.launcher3.taskbar.TaskbarActivityContext;
@@ -77,11 +78,16 @@
private boolean mBubblesShowingOnOverview;
private boolean mIsSysuiLocked;
+ private final float mHandleCenterFromScreenBottom;
+
@Nullable
private AnimatorSet mAnimator;
public BubbleStashController(TaskbarActivityContext activity) {
mActivity = activity;
+ // the handle is centered within the stashed taskbar area
+ mHandleCenterFromScreenBottom =
+ mActivity.getResources().getDimensionPixelSize(R.dimen.bubblebar_stashed_size) / 2f;
}
public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) {
@@ -266,7 +272,6 @@
*/
private AnimatorSet createStashAnimator(boolean isStashed, long duration) {
AnimatorSet animatorSet = new AnimatorSet();
- final float stashTranslation = (mUnstashedHeight - mStashedHeight) / 2f;
AnimatorSet fullLengthAnimatorSet = new AnimatorSet();
// Not exactly half and may overlap. See [first|second]HalfDurationScale below.
@@ -280,7 +285,8 @@
firstHalfDurationScale = 0.75f;
secondHalfDurationScale = 0.5f;
- fullLengthAnimatorSet.play(mIconTranslationYForStash.animateToValue(stashTranslation));
+ fullLengthAnimatorSet.play(
+ mIconTranslationYForStash.animateToValue(getStashTranslation()));
firstHalfAnimatorSet.playTogether(
mIconAlphaForStash.animateToValue(0),
@@ -329,6 +335,10 @@
return animatorSet;
}
+ private float getStashTranslation() {
+ return (mUnstashedHeight - mStashedHeight) / 2f;
+ }
+
private void onIsStashedChanged() {
mControllers.runAfterInit(() -> {
mHandleViewController.onIsStashedChanged();
@@ -336,7 +346,7 @@
});
}
- private float getBubbleBarTranslationYForTaskbar() {
+ public float getBubbleBarTranslationYForTaskbar() {
return -mActivity.getDeviceProfile().taskbarBottomMargin;
}
@@ -355,6 +365,25 @@
: getBubbleBarTranslationYForTaskbar();
}
+ /**
+ * The difference on the Y axis between the center of the handle and the center of the bubble
+ * bar.
+ */
+ public float getDiffBetweenHandleAndBarCenters() {
+ // the difference between the centers of the handle and the bubble bar is the difference
+ // between their distance from the bottom of the screen.
+
+ float barCenter = mBarViewController.getBubbleBarCollapsedHeight() / 2f;
+ return mHandleCenterFromScreenBottom - barCenter;
+ }
+
+ /** The distance the handle moves as part of the new bubble animation. */
+ public float getStashedHandleTranslationForNewBubbleAnimation() {
+ // the should move up to the top of the stashed taskbar area. it is centered within it so
+ // it should move the same distance as it is away from the bottom.
+ return -mHandleCenterFromScreenBottom;
+ }
+
/** Checks whether the motion event is over the stash handle. */
public boolean isEventOverStashHandle(MotionEvent ev) {
return mHandleViewController.isEventOverHandle(ev);
@@ -365,11 +394,6 @@
mHandleViewController.setBubbleBarLocation(bubbleBarLocation);
}
- /** Returns the x position of the center of the stashed handle. */
- public float getStashedHandleCenterX() {
- return mHandleViewController.getStashedHandleCenterX();
- }
-
/** Returns the [PhysicsAnimator] for the stashed handle view. */
public PhysicsAnimator<View> getStashedHandlePhysicsAnimator() {
return mHandleViewController.getPhysicsAnimator();
@@ -389,4 +413,14 @@
mIsStashed = false;
onIsStashedChanged();
}
+
+ /** Stashes the bubble bar immediately without animation. */
+ public void stashBubbleBarImmediate() {
+ mHandleViewController.setTranslationYForSwipe(0);
+ mIconAlphaForStash.setValue(0);
+ mIconTranslationYForStash.updateValue(getStashTranslation());
+ mIconScaleForStash.updateValue(STASHED_BAR_SCALE);
+ mIsStashed = true;
+ onIsStashedChanged();
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
index 6f1a093..91103d7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
@@ -251,11 +251,6 @@
return mStashedHandleAlpha;
}
- /** Returns the x position of the center of the stashed handle. */
- public float getStashedHandleCenterX() {
- return mStashedHandleBounds.exactCenterX();
- }
-
/**
* Creates and returns an Animator that updates the stashed handle shape and size.
* When stashed, the shape is a thin rounded pill. When unstashed, the shape morphs into
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
index da36944..a6d0ff8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt
@@ -40,20 +40,8 @@
private companion object {
/** The time to show the flyout. */
const val FLYOUT_DELAY_MS: Long = 2500
- /** The translation Y the new bubble will animate to. */
- const val BUBBLE_ANIMATION_BUBBLE_TRANSLATION_Y = -50f
- /** The initial translation Y value the new bubble is set to before the animation starts. */
- // TODO(liranb): get rid of this and calculate this based on the y-distance between the
- // bubble and the stash handle.
- const val BUBBLE_ANIMATION_TRANSLATION_Y_OFFSET = 50f
/** The initial scale Y value that the new bubble is set to before the animation starts. */
const val BUBBLE_ANIMATION_INITIAL_SCALE_Y = 0.3f
- /**
- * The distance the stashed handle will travel as it gets hidden as part of the new bubble
- * animation.
- */
- // TODO(liranb): calculate this based on the position of the views
- const val BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y = -20f
}
/** Wrapper around the animating bubble with its show and hide animations. */
@@ -105,166 +93,156 @@
if (animator.isRunning()) animator.cancel()
// the animation of a new bubble is divided into 2 parts. The first part shows the bubble
// and the second part hides it after a delay.
- val showAnimation = buildShowAnimation(bubbleView, b.key)
- val hideAnimation = buildHideAnimation(bubbleView)
+ val showAnimation = buildShowAnimation()
+ val hideAnimation = buildHideAnimation()
animatingBubble = AnimatingBubble(bubbleView, showAnimation, hideAnimation)
scheduler.post(showAnimation)
scheduler.postDelayed(FLYOUT_DELAY_MS, hideAnimation)
}
/**
- * Returns a lambda that starts the animation that shows the new bubble.
+ * Returns a [Runnable] that starts the animation that shows the new or updated bubble.
*
* Visually, the animation is divided into 2 parts. The stash handle starts animating up and
- * fading out and then the bubble starts animating up and fading in.
+ * fading out and then the bubble bar starts animating up and fading in.
*
- * To make the transition from the handle to the bubble smooth, the positions and movement of
- * the 2 views must be synchronized. To do that we use a single spring path along the Y axis,
- * starting from the handle's position to the eventual bubble's position. The path is split into
- * 3 parts.
+ * To make the transition from the handle to the bar smooth, the positions and movement of the 2
+ * views must be synchronized. To do that we use a single spring path along the Y axis, starting
+ * from the handle's position to the eventual bar's position. The path is split into 3 parts.
* 1. In the first part, we only animate the handle.
- * 1. In the second part the handle is fully hidden, and the bubble is animating in.
- * 1. The third part is the overshoot of the spring animation, where we make the bubble fully
+ * 2. In the second part the handle is fully hidden, and the bubble bar is animating in.
+ * 3. The third part is the overshoot of the spring animation, where we make the bubble fully
* visible which helps avoiding further updates when we re-enter the second part.
*/
- private fun buildShowAnimation(
- bubbleView: BubbleView,
- key: String,
- ) = Runnable {
- bubbleBarView.prepareForAnimatingBubbleWhileStashed(key)
- // calculate the initial translation x the bubble should have in order to align it with the
- // stash handle.
- val initialTranslationX =
- bubbleStashController.stashedHandleCenterX - bubbleView.centerXOnScreen
- // prepare the bubble for the animation
- bubbleView.alpha = 0f
- bubbleView.translationX = initialTranslationX
- bubbleView.scaleY = BUBBLE_ANIMATION_INITIAL_SCALE_Y
- bubbleView.visibility = VISIBLE
+ private fun buildShowAnimation() = Runnable {
+ // prepare the bubble bar for the animation
+ bubbleBarView.onAnimatingBubbleStarted()
+ bubbleBarView.visibility = VISIBLE
+ bubbleBarView.alpha = 0f
+ bubbleBarView.translationY = 0f
+ bubbleBarView.scaleX = 1f
+ bubbleBarView.scaleY = BUBBLE_ANIMATION_INITIAL_SCALE_Y
+ bubbleBarView.relativePivotY = 0.5f
+
+ // this is the offset between the center of the bubble bar and the center of the stash
+ // handle. when the handle becomes invisible and we start animating in the bubble bar,
+ // the translation y is offset by this value to make the transition from the handle to the
+ // bar smooth.
+ val offset = bubbleStashController.diffBetweenHandleAndBarCenters
+ val stashedHandleTranslationY =
+ bubbleStashController.stashedHandleTranslationForNewBubbleAnimation
// this is the total distance that both the stashed handle and the bubble will be traveling
- val totalTranslationY =
- BUBBLE_ANIMATION_BUBBLE_TRANSLATION_Y + BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y
+ // at the end of the animation the bubble bar will be positioned in the same place when it
+ // shows while we're in an app.
+ val totalTranslationY = bubbleStashController.bubbleBarTranslationYForTaskbar + offset
val animator = bubbleStashController.stashedHandlePhysicsAnimator
animator.setDefaultSpringConfig(springConfig)
animator.spring(DynamicAnimation.TRANSLATION_Y, totalTranslationY)
- animator.addUpdateListener { target, values ->
+ animator.addUpdateListener { handle, values ->
val ty = values[DynamicAnimation.TRANSLATION_Y]?.value ?: return@addUpdateListener
when {
- ty >= BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y -> {
+ ty >= stashedHandleTranslationY -> {
// we're in the first leg of the animation. only animate the handle. the bubble
- // remains hidden during this part of the animation
+ // bar remains hidden during this part of the animation
- // map the path [0, BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y] to [0,1]
- val fraction = ty / BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y
- target.alpha = 1 - fraction
+ // map the path [0, stashedHandleTranslationY] to [0,1]
+ val fraction = ty / stashedHandleTranslationY
+ handle.alpha = 1 - fraction
}
ty >= totalTranslationY -> {
// this is the second leg of the animation. the handle should be completely
- // hidden and the bubble should start animating in.
+ // hidden and the bubble bar should start animating in.
// it's possible that we're re-entering this leg because this is a spring
- // animation, so only set the alpha and scale for the bubble if we didn't
+ // animation, so only set the alpha and scale for the bubble bar if we didn't
// already fully animate in.
- target.alpha = 0f
- bubbleView.translationY = ty + BUBBLE_ANIMATION_TRANSLATION_Y_OFFSET
- if (bubbleView.alpha != 1f) {
- // map the path
- // [BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y, totalTranslationY]
- // to [0, 1]
+ handle.alpha = 0f
+ bubbleBarView.translationY = ty - offset
+ if (bubbleBarView.alpha != 1f) {
+ // map the path [stashedHandleTranslationY, totalTranslationY] to [0, 1]
val fraction =
- (ty - BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y) /
- BUBBLE_ANIMATION_BUBBLE_TRANSLATION_Y
- bubbleView.alpha = fraction
- bubbleView.scaleY =
+ (ty - stashedHandleTranslationY) /
+ (totalTranslationY - stashedHandleTranslationY)
+ bubbleBarView.alpha = fraction
+ bubbleBarView.scaleY =
BUBBLE_ANIMATION_INITIAL_SCALE_Y +
(1 - BUBBLE_ANIMATION_INITIAL_SCALE_Y) * fraction
}
}
else -> {
// we're past the target animated value, set the alpha and scale for the bubble
- // so that it's fully visible and no longer changing, but keep moving it along
- // the animation path
- bubbleView.alpha = 1f
- bubbleView.scaleY = 1f
- bubbleView.translationY = ty + BUBBLE_ANIMATION_TRANSLATION_Y_OFFSET
+ // bar so that it's fully visible and no longer changing, but keep moving it
+ // along the animation path
+ bubbleBarView.alpha = 1f
+ bubbleBarView.scaleY = 1f
+ bubbleBarView.translationY = ty - offset
}
}
}
animator.addEndListener { _, _, _, _, _, _, _ ->
- // the bubble is now fully settled in. make it touchable
- bubbleBarView.updateAnimatingBubbleBounds(
- bubbleView.left,
- bubbleView.top,
- bubbleView.width,
- bubbleView.height
- )
+ // the bubble bar is now fully settled in. update taskbar touch region so it's touchable
bubbleStashController.updateTaskbarTouchRegion()
}
animator.start()
}
/**
- * Returns a lambda that starts the animation that hides the new bubble.
+ * Returns a [Runnable] that starts the animation that hides the bubble bar.
*
* Similarly to the show animation, this is visually divided into 2 parts. We first animate the
- * bubble out, and then animate the stash handle in. At the end of the animation we reset the
- * values of the bubble.
+ * bubble bar out, and then animate the stash handle in. At the end of the animation we reset
+ * values of the bubble bar.
*
* This is a spring animation that goes along the same path of the show animation in the
* opposite order, and is split into 3 parts:
* 1. In the first part the bubble animates out.
- * 1. In the second part the bubble is fully hidden and the handle animates in.
- * 1. The third part is the overshoot. The handle is made fully visible.
+ * 2. In the second part the bubble bar is fully hidden and the handle animates in.
+ * 3. The third part is the overshoot. The handle is made fully visible.
*/
- private fun buildHideAnimation(bubbleView: BubbleView) = Runnable {
- // this is the total distance that both the stashed handle and the bubble will be traveling
- val totalTranslationY =
- BUBBLE_ANIMATION_BUBBLE_TRANSLATION_Y + BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y
+ private fun buildHideAnimation() = Runnable {
+ val offset = bubbleStashController.diffBetweenHandleAndBarCenters
+ val stashedHandleTranslationY =
+ bubbleStashController.stashedHandleTranslationForNewBubbleAnimation
+ // this is the total distance that both the stashed handle and the bar will be traveling
+ val totalTranslationY = bubbleStashController.bubbleBarTranslationYForTaskbar + offset
val animator = bubbleStashController.stashedHandlePhysicsAnimator
animator.setDefaultSpringConfig(springConfig)
animator.spring(DynamicAnimation.TRANSLATION_Y, 0f)
- animator.addUpdateListener { target, values ->
+ animator.addUpdateListener { handle, values ->
val ty = values[DynamicAnimation.TRANSLATION_Y]?.value ?: return@addUpdateListener
when {
- ty <= BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y -> {
- // this is the first leg of the animation. only animate the bubble. the handle
- // is hidden during this part
- bubbleView.translationY = ty + BUBBLE_ANIMATION_TRANSLATION_Y_OFFSET
- // map the path
- // [totalTranslationY, BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y]
- // to [0, 1]
- val fraction = (totalTranslationY - ty) / BUBBLE_ANIMATION_BUBBLE_TRANSLATION_Y
- bubbleView.alpha = 1 - fraction / 2
- bubbleView.scaleY = 1 - (1 - BUBBLE_ANIMATION_INITIAL_SCALE_Y) * fraction
+ ty <= stashedHandleTranslationY -> {
+ // this is the first leg of the animation. only animate the bubble bar. the
+ // handle is hidden during this part
+ bubbleBarView.translationY = ty - offset
+ // map the path [totalTranslationY, stashedHandleTranslationY] to [0, 1]
+ val fraction =
+ (totalTranslationY - ty) / (totalTranslationY - stashedHandleTranslationY)
+ bubbleBarView.alpha = 1 - fraction
+ bubbleBarView.scaleY = 1 - (1 - BUBBLE_ANIMATION_INITIAL_SCALE_Y) * fraction
}
ty <= 0 -> {
- // this is the second part of the animation. make the bubble invisible and
+ // this is the second part of the animation. make the bubble bar invisible and
// start fading in the handle, but don't update the alpha if it's already fully
// visible
- bubbleView.alpha = 0f
- if (target.alpha != 1f) {
- // map the path [BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y, 0] to [0, 1]
- val fraction =
- (BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y - ty) /
- BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y
- target.alpha = fraction
+ bubbleBarView.alpha = 0f
+ if (handle.alpha != 1f) {
+ // map the path [stashedHandleTranslationY, 0] to [0, 1]
+ val fraction = (stashedHandleTranslationY - ty) / stashedHandleTranslationY
+ handle.alpha = fraction
}
}
else -> {
// we reached the target value. set the alpha of the handle to 1
- target.alpha = 1f
+ handle.alpha = 1f
}
}
}
animator.addEndListener { _, _, _, _, _, _, _ ->
animatingBubble = null
- bubbleView.alpha = 0f
- bubbleView.translationY = 0f
- bubbleView.scaleY = 1f
- if (bubbleStashController.isStashed) {
- bubbleBarView.alpha = 0f
- }
+ bubbleStashController.stashBubbleBarImmediate()
bubbleBarView.onAnimatingBubbleCompleted()
+ bubbleBarView.relativePivotY = 1f
bubbleStashController.updateTaskbarTouchRegion()
}
animator.start()
@@ -275,14 +253,7 @@
val hideAnimation = animatingBubble?.hideAnimation ?: return
scheduler.cancel(hideAnimation)
bubbleBarView.onAnimatingBubbleCompleted()
+ bubbleBarView.relativePivotY = 1f
animatingBubble = null
}
}
-
-/** The X position in screen coordinates of the center of the bubble. */
-private val BubbleView.centerXOnScreen: Float
- get() {
- val screenCoordinates = IntArray(2)
- getLocationOnScreen(screenCoordinates)
- return screenCoordinates[0] + width / 2f
- }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 4acddee..4184ab2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -166,6 +166,8 @@
import com.android.launcher3.util.TouchController;
import com.android.launcher3.widget.LauncherWidgetHolder;
import com.android.quickstep.OverviewCommandHelper;
+import com.android.quickstep.OverviewComponentObserver;
+import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskUtils;
@@ -264,6 +266,10 @@
getDepthController(), getStatsLogManager(),
systemUiProxy, RecentsModel.INSTANCE.get(this),
() -> onStateBack());
+ RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(asContext());
+ // TODO(b/337863494): Explore use of the same OverviewComponentObserver across launcher
+ OverviewComponentObserver overviewComponentObserver = new OverviewComponentObserver(
+ asContext(), deviceState);
if (enableDesktopWindowingMode()) {
mDesktopRecentsTransitionController = new DesktopRecentsTransitionController(
getStateManager(), systemUiProxy, getIApplicationThread(),
@@ -272,7 +278,7 @@
overviewPanel.init(mActionsView, mSplitSelectStateController,
mDesktopRecentsTransitionController);
mSplitWithKeyboardShortcutController = new SplitWithKeyboardShortcutController(this,
- mSplitSelectStateController);
+ mSplitSelectStateController, overviewComponentObserver, deviceState);
mSplitToWorkspaceController = new SplitToWorkspaceController(this,
mSplitSelectStateController);
mActionsView.updateDimension(getDeviceProfile(), overviewPanel.getLastComputedTaskSize());
@@ -287,7 +293,8 @@
if (enableDesktopWindowingMode()) {
mDesktopVisibilityController = new DesktopVisibilityController(this);
mDesktopVisibilityController.registerSystemUiListener();
- mSplitSelectStateController.initSplitFromDesktopController(this);
+ mSplitSelectStateController.initSplitFromDesktopController(this,
+ overviewComponentObserver);
}
mHotseatPredictionController = new HotseatPredictionController(this);
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 0a02e99..a71e314 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -109,6 +109,8 @@
* Sets a listener for changes in {@link #isHomeAndOverviewSame()}
*/
public void setOverviewChangeListener(Consumer<Boolean> overviewChangeListener) {
+ // TODO(b/337861962): This method should be able to support multiple listeners instead of
+ // one so that we can reuse the same instance of this class across multiple places
mOverviewChangeListener = overviewChangeListener;
}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index b0b2589..fcf5ffc 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -16,7 +16,6 @@
package com.android.quickstep;
import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
-import static android.content.pm.PackageManager.FEATURE_PC;
import static com.android.launcher3.Flags.enableUnfoldStateAnimation;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -1385,8 +1384,7 @@
private boolean shouldEnableRunningTasksForDesktopMode() {
// TODO(b/335401172): unify DesktopMode checks in Launcher
- return (enableDesktopWindowingMode() && enableDesktopWindowingTaskbarRunningApps())
- || mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
+ return enableDesktopWindowingMode() && enableDesktopWindowingTaskbarRunningApps();
}
private boolean handleMessageAsync(Message msg) {
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index d881a1f..b79586b 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -53,6 +53,7 @@
import com.android.systemui.shared.recents.model.Task;
import java.util.ArrayList;
+import java.util.Arrays;
public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsState>
implements StateListener<RecentsState> {
@@ -144,8 +145,9 @@
@Override
public void setCurrentTask(int runningTaskViewId) {
super.setCurrentTask(runningTaskViewId);
- int runningTaskId = getTaskIdsForRunningTaskView()[0];
- if (mHomeTask != null && mHomeTask.key.id != runningTaskId) {
+ int[] runningTaskIds = getTaskIdsForRunningTaskView();
+ if (mHomeTask != null
+ && Arrays.stream(runningTaskIds).noneMatch(taskId -> taskId == mHomeTask.key.id)) {
mHomeTask = null;
setRunningTaskHidden(false);
}
@@ -182,13 +184,14 @@
// as well. This tile is never shown as we have setCurrentTaskHidden, but allows use to
// track the index of the next task appropriately, as if we are switching on any other app.
// TODO(b/195607777) Confirm home task info is front-most task and not mixed in with others
- int runningTaskId = getTaskIdsForRunningTaskView()[0];
- if (mHomeTask != null && mHomeTask.key.id == runningTaskId
+ int[] runningTaskIds = getTaskIdsForRunningTaskView();
+ if (mHomeTask != null
+ && Arrays.stream(runningTaskIds).allMatch(taskId -> taskId == mHomeTask.key.id)
&& !taskGroups.isEmpty()) {
// Check if the task list has running task
boolean found = false;
for (GroupTask group : taskGroups) {
- if (group.containsTask(runningTaskId)) {
+ if (Arrays.stream(runningTaskIds).allMatch(group::containsTask)) {
found = true;
break;
}
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index c257be6..df1879e 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -90,7 +90,6 @@
import com.android.quickstep.OverviewComponentObserver;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationController;
-import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.SplitSelectionListener;
@@ -646,8 +645,13 @@
}
}
- public void initSplitFromDesktopController(QuickstepLauncher launcher) {
- initSplitFromDesktopController(new SplitFromDesktopController(launcher));
+ /**
+ * Init {@code SplitFromDesktopController}
+ */
+ public void initSplitFromDesktopController(QuickstepLauncher launcher,
+ OverviewComponentObserver overviewComponentObserver) {
+ initSplitFromDesktopController(
+ new SplitFromDesktopController(launcher, overviewComponentObserver));
}
@VisibleForTesting
@@ -956,12 +960,10 @@
private ISplitSelectListener mSplitSelectListener;
private Drawable mAppIcon;
- public SplitFromDesktopController(QuickstepLauncher launcher) {
+ public SplitFromDesktopController(QuickstepLauncher launcher,
+ OverviewComponentObserver overviewComponentObserver) {
mLauncher = launcher;
- RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(
- launcher.getApplicationContext());
- mOverviewComponentObserver =
- new OverviewComponentObserver(launcher.getApplicationContext(), deviceState);
+ mOverviewComponentObserver = overviewComponentObserver;
mSplitPlaceholderSize = mLauncher.getResources().getDimensionPixelSize(
R.dimen.split_placeholder_size);
mSplitPlaceholderInset = mLauncher.getResources().getDimensionPixelSize(
diff --git a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
index 555bf21..85d4f4b 100644
--- a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
@@ -62,12 +62,13 @@
private final int mSplitPlaceholderInset;
public SplitWithKeyboardShortcutController(QuickstepLauncher launcher,
- SplitSelectStateController controller) {
+ SplitSelectStateController controller,
+ OverviewComponentObserver overviewComponentObserver,
+ RecentsAnimationDeviceState deviceState) {
mLauncher = launcher;
mController = controller;
- mDeviceState = new RecentsAnimationDeviceState(launcher.getApplicationContext());
- mOverviewComponentObserver = new OverviewComponentObserver(launcher.getApplicationContext(),
- mDeviceState);
+ mDeviceState = deviceState;
+ mOverviewComponentObserver = overviewComponentObserver;
mSplitPlaceholderSize = mLauncher.getResources().getDimensionPixelSize(
R.dimen.split_placeholder_size);
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
index c100687..e797819 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -204,18 +204,14 @@
}
private void updateTaskIdContainer() {
- // TODO(b/249371338): TaskView expects the array to have at least 2 elements.
- // At least 2 elements in the array
- mTaskIdContainer = new int[Math.max(mTasks.size(), 2)];
+ mTaskIdContainer = new int[mTasks.size()];
for (int i = 0; i < mTasks.size(); i++) {
mTaskIdContainer[i] = mTasks.get(i).key.id;
}
}
private void updateTaskIdAttributeContainer() {
- // TODO(b/249371338): TaskView expects the array to have at least 2 elements.
- // At least 2 elements in the array
- mTaskIdAttributeContainer = new TaskIdAttributeContainer[Math.max(mTasks.size(), 2)];
+ mTaskIdAttributeContainer = new TaskIdAttributeContainer[mTasks.size()];
for (int i = 0; i < mTasks.size(); i++) {
Task task = mTasks.get(i);
TaskThumbnailViewDeprecated thumbnailView = mSnapshotViewMap.get(task.key.id);
@@ -248,12 +244,6 @@
}
@Override
- public boolean containsTaskId(int taskId) {
- // Thumbnail map contains taskId -> thumbnail map. Use the keys for contains
- return mSnapshotViewMap.contains(taskId);
- }
-
- @Override
public void onTaskListVisibilityChanged(boolean visible, int changes) {
cancelPendingLoadTasks();
if (visible) {
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index 9a4434a..c7a4203 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -6,6 +6,7 @@
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.quickstep.util.SplitScreenUtils.convertLauncherSplitBoundsToShell;
+import android.app.ActivityTaskManager;
import android.content.Context;
import android.graphics.Point;
import android.graphics.PointF;
@@ -46,6 +47,7 @@
import java.util.Arrays;
import java.util.HashMap;
+import java.util.Optional;
import java.util.function.Consumer;
/**
@@ -129,9 +131,11 @@
@Nullable SplitBounds splitBoundsConfig) {
super.bind(primary, orientedState);
mSecondaryTask = secondary;
- mTaskIdContainer[1] = secondary.key.id;
- mTaskIdAttributeContainer[1] = new TaskIdAttributeContainer(secondary, mSnapshotView2,
- mIconView2, STAGE_POSITION_BOTTOM_OR_RIGHT);
+ mTaskIdContainer = new int[]{mTaskIdContainer[0], secondary.key.id};
+ mTaskIdAttributeContainer = new TaskIdAttributeContainer[]{
+ mTaskIdAttributeContainer[0],
+ new TaskIdAttributeContainer(secondary, mSnapshotView2,
+ mIconView2, STAGE_POSITION_BOTTOM_OR_RIGHT)};
mTaskIdAttributeContainer[0].setStagePosition(
SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT);
mSnapshotView2.bind(secondary);
@@ -154,6 +158,9 @@
public void setUpShowAllInstancesListener() {
// sets up the listener for the left/top task
super.setUpShowAllInstancesListener();
+ if (mTaskIdAttributeContainer.length < 2) {
+ return;
+ }
// right/bottom task's base package name
String taskPackageName = mTaskIdAttributeContainer[1].getTask().key.getPackageName();
@@ -308,16 +315,20 @@
}
@Override
- public boolean containsTaskId(int taskId) {
- return (mTask != null && mTask.key.id == taskId)
- || (mSecondaryTask != null && mSecondaryTask.key.id == taskId);
- }
-
- @Override
public TaskThumbnailViewDeprecated[] getThumbnails() {
return new TaskThumbnailViewDeprecated[]{mTaskThumbnailViewDeprecated, mSnapshotView2};
}
+ /**
+ * Returns taskId that split selection was initiated with,
+ * {@link ActivityTaskManager#INVALID_TASK_ID} if no tasks in this TaskView are part of
+ * split selection
+ */
+ protected int getThisTaskCurrentlyInSplitSelection() {
+ int initialTaskId = getRecentsView().getSplitSelectController().getInitialTaskId();
+ return containsTaskId(initialTaskId) ? initialTaskId : INVALID_TASK_ID;
+ }
+
@Override
protected int getLastSelectedChildTaskIndex() {
SplitSelectStateController splitSelectController =
@@ -382,13 +393,15 @@
} else {
// Currently being split with this taskView, let the non-split selected thumbnail
// take up full thumbnail area
- TaskIdAttributeContainer container =
- mTaskIdAttributeContainer[initSplitTaskId == mTask.key.id ? 1 : 0];
- container.getThumbnailView().measure(widthMeasureSpec,
- View.MeasureSpec.makeMeasureSpec(
- heightSize -
- mContainer.getDeviceProfile().overviewTaskThumbnailTopMarginPx,
- MeasureSpec.EXACTLY));
+ Optional<TaskIdAttributeContainer> nonSplitContainer = Arrays.stream(
+ mTaskIdAttributeContainer).filter(
+ container -> container.getTask().key.id != initSplitTaskId).findAny();
+ nonSplitContainer.ifPresent(
+ taskIdAttributeContainer -> taskIdAttributeContainer.getThumbnailView().measure(
+ widthMeasureSpec, MeasureSpec.makeMeasureSpec(
+ heightSize - mContainer.getDeviceProfile()
+ .overviewTaskThumbnailTopMarginPx,
+ MeasureSpec.EXACTLY)));
}
if (!enableOverviewIconMenu()) {
updateIconPlacement();
@@ -529,7 +542,7 @@
mDigitalWellBeingToast.setBannerVisibility(visibility);
mSnapshotView2.setVisibility(visibility);
mDigitalWellBeingToast2.setBannerVisibility(visibility);
- } else if (taskId == getTaskIds()[0]) {
+ } else if (mTaskIdContainer.length > 0 && mTaskIdContainer[0] == taskId) {
mTaskThumbnailViewDeprecated.setVisibility(visibility);
mDigitalWellBeingToast.setBannerVisibility(visibility);
} else {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index cc5ce01..62fa6c8 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -626,7 +626,6 @@
*/
protected int mRunningTaskViewId = -1;
private int mTaskViewIdCount;
- private final int[] INVALID_TASK_IDS = new int[]{-1, -1};
protected boolean mRunningTaskTileHidden;
@Nullable
private Task[] mTmpRunningTasks;
@@ -1413,7 +1412,7 @@
*/
@Nullable
public TaskView getTaskViewByTaskIds(int[] taskIds) {
- if (!hasAnyValidTaskIds(taskIds)) {
+ if (!hasAllValidTaskIds(taskIds)) {
return null;
}
@@ -1432,9 +1431,11 @@
return null;
}
- /** Returns false if {@code taskIds} is null or contains invalid values, true otherwise */
- private boolean hasAnyValidTaskIds(int[] taskIds) {
- return taskIds != null && !Arrays.equals(taskIds, INVALID_TASK_IDS);
+ /** Returns false if {@code taskIds} is null or contains any invalid values, true otherwise */
+ private boolean hasAllValidTaskIds(int[] taskIds) {
+ return taskIds != null
+ && taskIds.length > 0
+ && Arrays.stream(taskIds).noneMatch(taskId -> taskId == INVALID_TASK_ID);
}
public void setOverviewStateEnabled(boolean enabled) {
@@ -1726,10 +1727,12 @@
return;
}
- int[] currentTaskId = INVALID_TASK_IDS;
+ int[] currentTaskIds;
TaskView currentTaskView = getTaskViewAt(mCurrentPage);
if (currentTaskView != null && currentTaskView.getTask() != null) {
- currentTaskId = currentTaskView.getTaskIds();
+ currentTaskIds = currentTaskView.getTaskIds();
+ } else {
+ currentTaskIds = new int[0];
}
// Unload existing visible task data
@@ -1741,8 +1744,8 @@
// Save running task ID if it exists before rebinding all taskViews, otherwise the task from
// the runningTaskView currently bound could get assigned to another TaskView
- int[] runningTaskId = getTaskIdsForTaskViewId(mRunningTaskViewId);
- int[] focusedTaskId = getTaskIdsForTaskViewId(mFocusedTaskViewId);
+ int[] runningTaskIds = getTaskIdsForTaskViewId(mRunningTaskViewId);
+ int[] focusedTaskIds = getTaskIdsForTaskViewId(mFocusedTaskViewId);
// Reset the focused task to avoiding initializing TaskViews layout as focused task during
// binding. The focused task view will be updated after all the TaskViews are bound.
@@ -1825,7 +1828,7 @@
}
// Keep same previous focused task
- TaskView newFocusedTaskView = getTaskViewByTaskIds(focusedTaskId);
+ TaskView newFocusedTaskView = getTaskViewByTaskIds(focusedTaskIds);
// If the list changed, maybe the focused task doesn't exist anymore
if (newFocusedTaskView == null && getTaskViewCount() > 0) {
newFocusedTaskView = getTaskViewAt(0);
@@ -1836,10 +1839,10 @@
updateChildTaskOrientations();
TaskView newRunningTaskView = null;
- if (hasAnyValidTaskIds(runningTaskId)) {
+ if (hasAllValidTaskIds(runningTaskIds)) {
// Update mRunningTaskViewId to be the new TaskView that was assigned by binding
// the full list of tasks to taskViews
- newRunningTaskView = getTaskViewByTaskIds(runningTaskId);
+ newRunningTaskView = getTaskViewByTaskIds(runningTaskIds);
if (newRunningTaskView != null) {
setRunningTaskViewId(newRunningTaskView.getTaskViewId());
} else {
@@ -1859,8 +1862,8 @@
if (mNextPage != INVALID_PAGE) {
// Restore mCurrentPage but don't call setCurrentPage() as that clobbers the scroll.
mCurrentPage = previousCurrentPage;
- if (hasAnyValidTaskIds(currentTaskId)) {
- currentTaskView = getTaskViewByTaskIds(currentTaskId);
+ if (hasAllValidTaskIds(currentTaskIds)) {
+ currentTaskView = getTaskViewByTaskIds(currentTaskIds);
if (currentTaskView != null) {
targetPage = indexOfChild(currentTaskView);
}
@@ -1869,7 +1872,7 @@
targetPage = previousFocusedPage;
} else {
// Set the current page to the running task, but not if settling on new task.
- if (hasAnyValidTaskIds(runningTaskId)) {
+ if (hasAllValidTaskIds(runningTaskIds)) {
targetPage = indexOfChild(newRunningTaskView);
} else if (getTaskViewCount() > 0) {
targetPage = indexOfChild(requireTaskViewAt(0));
@@ -1969,7 +1972,8 @@
public void resetTaskVisuals() {
for (int i = getTaskViewCount() - 1; i >= 0; i--) {
TaskView taskView = requireTaskViewAt(i);
- if (mIgnoreResetTaskId != taskView.getTaskIds()[0]) {
+ if (Arrays.stream(taskView.getTaskIds()).noneMatch(
+ taskId -> taskId == mIgnoreResetTaskId)) {
taskView.resetViewTransforms();
taskView.setIconScaleAndDim(mTaskIconScaledDown ? 0 : 1);
taskView.setStableAlpha(mContentAlpha);
@@ -2349,7 +2353,7 @@
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView taskView = requireTaskViewAt(i);
TaskIdAttributeContainer[] containers = taskView.getTaskIdAttributeContainers();
- if (containers[0] == null && containers[1] == null) {
+ if (containers.length == 0) {
continue;
}
int index = indexOfChild(taskView);
@@ -2504,7 +2508,7 @@
// For now 2 distinct task IDs is max for split screen
TaskView runningTaskView = getTaskViewFromTaskViewId(taskViewId);
if (runningTaskView == null) {
- return INVALID_TASK_IDS;
+ return new int[0];
}
return runningTaskView.getTaskIds();
@@ -2742,22 +2746,19 @@
* Returns true if we should add a stub taskView for the running task id
*/
protected boolean shouldAddStubTaskView(Task[] runningTasks) {
- TaskView taskView = getTaskViewByTaskId(runningTasks[0].key.id);
- if (taskView == null) {
- // No TaskView found, add a stub task.
- return true;
- }
-
- if (runningTasks.length > 1) {
- // Ensure all taskIds matches the TaskView, otherwise add a stub task.
- return Arrays.stream(runningTasks).anyMatch(
- runningTask -> !taskView.containsTaskId(runningTask.key.id));
+ int[] runningTaskIds = Arrays.stream(runningTasks).mapToInt(task -> task.key.id).toArray();
+ TaskView matchingTaskView = null;
+ if (hasDesktopTask(runningTasks) && runningTaskIds.length == 1) {
+ // TODO(b/249371338): Unsure if it's expected, desktop runningTasks only have a single
+ // taskId, therefore we match any DesktopTaskView that contains the runningTaskId.
+ TaskView taskview = getTaskViewByTaskId(runningTaskIds[0]);
+ if (taskview instanceof DesktopTaskView) {
+ matchingTaskView = taskview;
+ }
} else {
- // Ensure the TaskView only contains a single taskId, or is a DesktopTask,
- // otherwise add a stub task.
- // TODO(b/249371338): Figure out why DesktopTask only have a single runningTask.
- return taskView.containsMultipleTasks() && !taskView.isDesktopTask();
+ matchingTaskView = getTaskViewByTaskIds(runningTaskIds);
}
+ return matchingTaskView == null;
}
/**
@@ -4280,13 +4281,10 @@
alpha = Utilities.boundToRange(alpha, 0, 1);
mContentAlpha = alpha;
- int runningTaskId = getTaskIdsForRunningTaskView()[0];
+ TaskView runningTaskView = getRunningTaskView();
for (int i = getTaskViewCount() - 1; i >= 0; i--) {
TaskView child = requireTaskViewAt(i);
- int[] childTaskIds = child.getTaskIds();
- if (runningTaskId != INVALID_TASK_ID
- && mRunningTaskTileHidden
- && (childTaskIds[0] == runningTaskId || childTaskIds[1] == runningTaskId)) {
+ if (runningTaskView != null && mRunningTaskTileHidden && child == runningTaskView) {
continue;
}
child.setStableAlpha(alpha);
@@ -4760,7 +4758,7 @@
// Prevent dismissing whole task if we're only initiating from one of 2 tasks in split pair
mSplitSelectStateController.setDismissingFromSplitPair(mSplitHiddenTaskView != null
- && mSplitHiddenTaskView.containsMultipleTasks());
+ && mSplitHiddenTaskView instanceof GroupedTaskView);
mSplitSelectStateController.setInitialTaskSelect(splitSelectSource.intent,
splitSelectSource.position.stagePosition, splitSelectSource.itemInfo,
splitSelectSource.splitEvent, splitSelectSource.alreadyRunningTaskId);
@@ -4781,14 +4779,14 @@
mSplitSelectStateController.isAnimateCurrentTaskDismissal();
boolean isInitiatingTaskViewSplitPair =
mSplitSelectStateController.isDismissingFromSplitPair();
- if (isInitiatingSplitFromTaskView && isInitiatingTaskViewSplitPair) {
+ if (isInitiatingSplitFromTaskView && isInitiatingTaskViewSplitPair
+ && mSplitHiddenTaskView instanceof GroupedTaskView) {
// Splitting from Overview for split pair task
createInitialSplitSelectAnimation(builder);
// Animate pair thumbnail into full thumbnail
- boolean primaryTaskSelected =
- mSplitHiddenTaskView.getTaskIdAttributeContainers()[0].getTask().key.id ==
- mSplitSelectStateController.getInitialTaskId();
+ boolean primaryTaskSelected = mSplitHiddenTaskView.getTaskIds()[0]
+ == mSplitSelectStateController.getInitialTaskId();
TaskIdAttributeContainer taskIdAttributeContainer = mSplitHiddenTaskView
.getTaskIdAttributeContainers()[primaryTaskSelected ? 1 : 0];
TaskThumbnailViewDeprecated thumbnail = taskIdAttributeContainer.getThumbnailView();
@@ -5239,7 +5237,8 @@
mPendingAnimation.addOnFrameCallback(this::redrawLiveTile);
mPendingAnimation.addEndListener(isSuccess -> {
if (isSuccess) {
- if (tv.getTaskIds()[1] != -1 && mRemoteTargetHandles != null) {
+ if (tv instanceof GroupedTaskView && hasAllValidTaskIds(tv.getTaskIds())
+ && mRemoteTargetHandles != null) {
// TODO(b/194414938): make this part of the animations instead.
TaskViewUtils.createSplitAuxiliarySurfacesAnimator(
mRemoteTargetHandles[0].getTransformParams().getTargetSet().nonApps,
@@ -5887,8 +5886,7 @@
}
taskView.setShowScreenshot(true);
- for (TaskIdAttributeContainer container :
- taskView.getTaskIdAttributeContainers()) {
+ for (TaskIdAttributeContainer container : taskView.getTaskIdAttributeContainers()) {
if (container == null) {
continue;
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index f766f70..43965b2 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -16,7 +16,6 @@
package com.android.quickstep.views;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.widget.Toast.LENGTH_SHORT;
@@ -48,7 +47,6 @@
import android.animation.ObjectAnimator;
import android.annotation.IdRes;
import android.app.ActivityOptions;
-import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
@@ -127,6 +125,7 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.Optional;
import java.util.function.Consumer;
import java.util.stream.Stream;
@@ -150,7 +149,8 @@
*/
@Retention(SOURCE)
@IntDef({FLAG_UPDATE_ALL, FLAG_UPDATE_ICON, FLAG_UPDATE_THUMBNAIL, FLAG_UPDATE_CORNER_RADIUS})
- public @interface TaskDataChanges {}
+ public @interface TaskDataChanges {
+ }
/**
* Type of task view
@@ -371,12 +371,9 @@
private float mStableAlpha = 1;
private int mTaskViewId = -1;
- /**
- * Index 0 will contain taskID of left/top task, index 1 will contain taskId of bottom/right
- */
- protected int[] mTaskIdContainer = new int[]{-1, -1};
+ protected int[] mTaskIdContainer = new int[0];
protected TaskIdAttributeContainer[] mTaskIdAttributeContainer =
- new TaskIdAttributeContainer[2];
+ new TaskIdAttributeContainer[0];
private boolean mShowScreenshot;
private boolean mBorderEnabled;
@@ -395,9 +392,11 @@
private boolean mIsClickableAsLiveTile = true;
- @Nullable private final BorderAnimator mFocusBorderAnimator;
+ @Nullable
+ private final BorderAnimator mFocusBorderAnimator;
- @Nullable private final BorderAnimator mHoverBorderAnimator;
+ @Nullable
+ private final BorderAnimator mHoverBorderAnimator;
public TaskView(Context context) {
this(context, null);
@@ -674,10 +673,10 @@
public void bind(Task task, RecentsOrientedState orientedState) {
cancelPendingLoadTasks();
mTask = task;
- mTaskIdContainer[0] = mTask.key.id;
- mTaskIdAttributeContainer[0] = new TaskIdAttributeContainer(task,
- mTaskThumbnailViewDeprecated, mIconView,
- STAGE_POSITION_UNDEFINED);
+ mTaskIdContainer = new int[]{mTask.key.id};
+ mTaskIdAttributeContainer = new TaskIdAttributeContainer[]{
+ new TaskIdAttributeContainer(task, mTaskThumbnailViewDeprecated, mIconView,
+ STAGE_POSITION_UNDEFINED)};
if (enableRefactorTaskThumbnail()) {
bindTaskThumbnailView();
} else {
@@ -696,6 +695,9 @@
* Sets up an on-click listener and the visibility for show_windows icon on top of the task.
*/
public void setUpShowAllInstancesListener() {
+ if (mTaskIdAttributeContainer.length == 0) {
+ return;
+ }
String taskPackageName = mTaskIdAttributeContainer[0].mTask.key.getPackageName();
// icon of the top/left task
@@ -751,19 +753,18 @@
* Check if given {@code taskId} is tracked in this view
*/
public boolean containsTaskId(int taskId) {
- return mTask != null && mTask.key.id == taskId;
+ return Arrays.stream(mTaskIdContainer).anyMatch(myTaskId -> myTaskId == taskId);
}
/**
- * @return integer array of two elements to be size consistent with max number of tasks possible
- * index 0 will contain the taskId, index 1 will be -1 indicating a null taskID value
+ * Returns a copy of integer array containing taskIds of all tasks in the TaskView.
*/
public int[] getTaskIds() {
return Arrays.copyOf(mTaskIdContainer, mTaskIdContainer.length);
}
public boolean containsMultipleTasks() {
- return mTaskIdContainer[1] != -1;
+ return mTaskIdContainer.length > 1;
}
/**
@@ -833,25 +834,6 @@
return super.dispatchTouchEvent(ev);
}
- /**
- * @return taskId that split selection was initiated with,
- * {@link ActivityTaskManager#INVALID_TASK_ID} if no tasks in this TaskView are part of
- * split selection
- */
- protected int getThisTaskCurrentlyInSplitSelection() {
- SplitSelectStateController splitSelectController =
- getRecentsView().getSplitSelectController();
- int initSplitTaskId = INVALID_TASK_ID;
- for (TaskIdAttributeContainer container : getTaskIdAttributeContainers()) {
- int taskId = container.getTask().key.id;
- if (taskId == splitSelectController.getInitialTaskId()) {
- initSplitTaskId = taskId;
- break;
- }
- }
- return initSplitTaskId;
- }
-
private void onClick(View view) {
if (getTask() == null) {
Log.d("b/310064698", "onClick - task is null");
@@ -873,10 +855,13 @@
/**
* @return {@code true} if user is already in split select mode and this tap was to choose the
- * second app. {@code false} otherwise
+ * second app. {@code false} otherwise
*/
protected boolean confirmSecondSplitSelectApp() {
int index = getLastSelectedChildTaskIndex();
+ if (index >= mTaskIdAttributeContainer.length) {
+ return false;
+ }
TaskIdAttributeContainer container = mTaskIdAttributeContainer[index];
if (container != null) {
return getRecentsView().confirmSplitSelect(this, container.getTask(),
@@ -898,6 +883,7 @@
/**
* Starts the task associated with this view and animates the startup.
+ *
* @return CompletionStage to indicate the animation completion or null if the launch failed.
*/
@Nullable
@@ -905,7 +891,7 @@
if (mTask != null) {
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
- ActivityOptionsWrapper opts = mContainer.getActivityLaunchOptions(this, null);
+ ActivityOptionsWrapper opts = mContainer.getActivityLaunchOptions(this, null);
opts.options.setLaunchDisplayId(
getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());
if (ActivityManagerWrapper.getInstance()
@@ -1096,6 +1082,7 @@
/**
* See {@link TaskDataChanges}
+ *
* @param visible If this task view will be visible to the user in overview or hidden
*/
public void onTaskListVisibilityChanged(boolean visible) {
@@ -1104,6 +1091,7 @@
/**
* See {@link TaskDataChanges}
+ *
* @param visible If this task view will be visible to the user in overview or hidden
*/
public void onTaskListVisibilityChanged(boolean visible, @TaskDataChanges int changes) {
@@ -1192,29 +1180,34 @@
}
protected boolean showTaskMenuWithContainer(TaskViewIcon iconView) {
- TaskIdAttributeContainer menuContainer =
- mTaskIdAttributeContainer[iconView == mIconView ? 0 : 1];
+ Optional<TaskIdAttributeContainer> menuContainer = Arrays.stream(
+ mTaskIdAttributeContainer).filter(
+ container -> container.getIconView() == iconView).findAny();
+ if (menuContainer.isEmpty()) {
+ return false;
+ }
DeviceProfile dp = mContainer.getDeviceProfile();
if (enableOverviewIconMenu() && iconView instanceof IconAppChipView) {
((IconAppChipView) iconView).revealAnim(/* isRevealing= */ true);
- return TaskMenuView.showForTask(menuContainer,
+ return TaskMenuView.showForTask(menuContainer.get(),
() -> ((IconAppChipView) iconView).revealAnim(/* isRevealing= */ false));
} else if (dp.isTablet) {
int alignedOptionIndex = 0;
- if (getRecentsView().isOnGridBottomRow(menuContainer.getTaskView()) && dp.isLandscape) {
+ if (getRecentsView().isOnGridBottomRow(menuContainer.get().getTaskView())
+ && dp.isLandscape) {
if (Flags.enableGridOnlyOverview()) {
// With no focused task, there is less available space below the tasks, so align
// the arrow to the third option in the menu.
alignedOptionIndex = 2;
- } else {
+ } else {
// Bottom row of landscape grid aligns arrow to second option to avoid clipping
alignedOptionIndex = 1;
}
}
- return TaskMenuViewWithArrow.Companion.showForTask(menuContainer,
+ return TaskMenuViewWithArrow.Companion.showForTask(menuContainer.get(),
alignedOptionIndex);
} else {
- return TaskMenuView.showForTask(menuContainer);
+ return TaskMenuView.showForTask(menuContainer.get());
}
}
@@ -1668,9 +1661,6 @@
final Context context = getContext();
for (TaskIdAttributeContainer taskContainer : mTaskIdAttributeContainer) {
- if (taskContainer == null) {
- continue;
- }
for (SystemShortcut s : TraceHelper.allowIpcs(
"TV.a11yInfo", () -> getEnabledShortcuts(this, taskContainer))) {
info.addAction(s.createAccessibilityAction(context));
@@ -1706,9 +1696,6 @@
}
for (TaskIdAttributeContainer taskContainer : mTaskIdAttributeContainer) {
- if (taskContainer == null) {
- continue;
- }
for (SystemShortcut s : getEnabledShortcuts(this,
taskContainer)) {
if (s.hasHandlerForAction(action)) {
@@ -1907,13 +1894,13 @@
private int getRootViewDisplayId() {
- Display display = getRootView().getDisplay();
+ Display display = getRootView().getDisplay();
return display != null ? display.getDisplayId() : DEFAULT_DISPLAY;
}
/**
- * Sets visibility for the thumbnail and associated elements (DWB banners and action chips).
- * IconView is unaffected.
+ * Sets visibility for the thumbnail and associated elements (DWB banners and action chips).
+ * IconView is unaffected.
*
* @param taskId is only used when setting visibility to a non-{@link View#VISIBLE} value
*/
@@ -1970,7 +1957,8 @@
}
@Override
- public void close() { }
+ public void close() {
+ }
}
public class TaskIdAttributeContainer {
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 3d8484d..f46fdac 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
@@ -22,7 +22,6 @@
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import android.view.View
-import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import android.widget.FrameLayout
import androidx.core.graphics.drawable.toBitmap
@@ -45,6 +44,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@SmallTest
@@ -87,6 +87,12 @@
val bubbleStashController = mock<BubbleStashController>()
whenever(bubbleStashController.isStashed).thenReturn(true)
+ whenever(bubbleStashController.diffBetweenHandleAndBarCenters)
+ .thenReturn(DIFF_BETWEEN_HANDLE_AND_BAR_CENTERS)
+ whenever(bubbleStashController.stashedHandleTranslationForNewBubbleAnimation)
+ .thenReturn(HANDLE_TRANSLATION)
+ whenever(bubbleStashController.bubbleBarTranslationYForTaskbar)
+ .thenReturn(BAR_TRANSLATION_Y_FOR_TASKBAR)
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
@@ -104,13 +110,13 @@
PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(DynamicAnimation.TRANSLATION_Y)
assertThat(handle.alpha).isEqualTo(0)
- assertThat(handle.translationY).isEqualTo(-70)
- assertThat(overflowView.visibility).isEqualTo(INVISIBLE)
+ assertThat(handle.translationY)
+ .isEqualTo(DIFF_BETWEEN_HANDLE_AND_BAR_CENTERS + BAR_TRANSLATION_Y_FOR_TASKBAR)
assertThat(bubbleBarView.visibility).isEqualTo(VISIBLE)
- assertThat(bubbleView.visibility).isEqualTo(VISIBLE)
- assertThat(bubbleView.alpha).isEqualTo(1)
- assertThat(bubbleView.translationY).isEqualTo(-20)
- assertThat(bubbleView.scaleY).isEqualTo(1)
+ assertThat(bubbleBarView.scaleX).isEqualTo(1)
+ assertThat(bubbleBarView.scaleY).isEqualTo(1)
+ assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_TASKBAR)
+ assertThat(bubbleBarView.isAnimatingNewBubble).isTrue()
// execute the hide bubble animation
assertThat(animatorScheduler.delayedBlock).isNotNull()
@@ -120,14 +126,11 @@
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(DynamicAnimation.TRANSLATION_Y)
- assertThat(bubbleView.alpha).isEqualTo(1)
- assertThat(bubbleView.visibility).isEqualTo(VISIBLE)
- assertThat(bubbleView.translationY).isEqualTo(0)
- assertThat(bubbleBarView.alpha).isEqualTo(0)
- assertThat(overflowView.alpha).isEqualTo(1)
- assertThat(overflowView.visibility).isEqualTo(VISIBLE)
assertThat(handle.alpha).isEqualTo(1)
assertThat(handle.translationY).isEqualTo(0)
+ assertThat(bubbleBarView.alpha).isEqualTo(0)
+ assertThat(bubbleBarView.isAnimatingNewBubble).isFalse()
+ verify(bubbleStashController).stashBubbleBarImmediate()
}
@Test
@@ -158,6 +161,12 @@
val bubbleStashController = mock<BubbleStashController>()
whenever(bubbleStashController.isStashed).thenReturn(true)
+ whenever(bubbleStashController.diffBetweenHandleAndBarCenters)
+ .thenReturn(DIFF_BETWEEN_HANDLE_AND_BAR_CENTERS)
+ whenever(bubbleStashController.stashedHandleTranslationForNewBubbleAnimation)
+ .thenReturn(HANDLE_TRANSLATION)
+ whenever(bubbleStashController.bubbleBarTranslationYForTaskbar)
+ .thenReturn(BAR_TRANSLATION_Y_FOR_TASKBAR)
val handle = View(context)
val handleAnimator = PhysicsAnimator.getInstance(handle)
@@ -175,13 +184,15 @@
PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(DynamicAnimation.TRANSLATION_Y)
assertThat(handle.alpha).isEqualTo(0)
- assertThat(handle.translationY).isEqualTo(-70)
- assertThat(overflowView.visibility).isEqualTo(INVISIBLE)
+ assertThat(handle.translationY)
+ .isEqualTo(DIFF_BETWEEN_HANDLE_AND_BAR_CENTERS + BAR_TRANSLATION_Y_FOR_TASKBAR)
assertThat(bubbleBarView.visibility).isEqualTo(VISIBLE)
- assertThat(bubbleView.visibility).isEqualTo(VISIBLE)
- assertThat(bubbleView.alpha).isEqualTo(1)
- assertThat(bubbleView.translationY).isEqualTo(-20)
- assertThat(bubbleView.scaleY).isEqualTo(1)
+ assertThat(bubbleBarView.scaleX).isEqualTo(1)
+ assertThat(bubbleBarView.scaleY).isEqualTo(1)
+ assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_TASKBAR)
+ assertThat(bubbleBarView.isAnimatingNewBubble).isTrue()
+
+ verify(bubbleStashController).updateTaskbarTouchRegion()
// verify the hide bubble animation is pending
assertThat(animatorScheduler.delayedBlock).isNotNull()
@@ -189,11 +200,9 @@
animator.onBubbleClickedWhileAnimating()
assertThat(animatorScheduler.delayedBlock).isNull()
- assertThat(overflowView.visibility).isEqualTo(VISIBLE)
- assertThat(overflowView.alpha).isEqualTo(1)
- assertThat(bubbleView.alpha).isEqualTo(1)
- assertThat(bubbleView.visibility).isEqualTo(VISIBLE)
- assertThat(bubbleBarView.background).isNotNull()
+ assertThat(bubbleBarView.alpha).isEqualTo(1)
+ assertThat(bubbleBarView.visibility).isEqualTo(VISIBLE)
+ assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_TASKBAR)
assertThat(bubbleBarView.isAnimatingNewBubble).isFalse()
}
@@ -217,3 +226,7 @@
}
}
}
+
+private const val DIFF_BETWEEN_HANDLE_AND_BAR_CENTERS = -20f
+private const val HANDLE_TRANSLATION = -30f
+private const val BAR_TRANSLATION_Y_FOR_TASKBAR = -50f
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsControllerTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsControllerTest.kt
index 2cfcf38..4fafde8 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsControllerTest.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsControllerTest.kt
@@ -23,7 +23,6 @@
import android.os.Process
import android.os.UserHandle
import android.testing.AndroidTestingRunner
-import android.util.SparseArray
import com.android.launcher3.model.data.AppInfo
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.statehandlers.DesktopVisibilityController
@@ -55,7 +54,9 @@
super.setup()
userHandle = Process.myUserHandle()
taskbarRunningAppsController =
- DesktopTaskbarRunningAppsController(mockRecentsModel, mockDesktopVisibilityController)
+ DesktopTaskbarRunningAppsController(mockRecentsModel) {
+ mockDesktopVisibilityController
+ }
taskbarRunningAppsController.init(taskbarControllers)
taskbarRunningAppsController.setApps(
ALL_APP_PACKAGES.map { createTestAppInfo(packageName = it) }.toTypedArray()
@@ -63,20 +64,13 @@
}
@Test
- fun updateHotseatItemInfos_null_returnsNull() {
- assertThat(taskbarRunningAppsController.updateHotseatItemInfos(/* hotseatItems= */ null))
- .isNull()
- }
-
- @Test
fun updateHotseatItemInfos_notInDesktopMode_returnsExistingHotseatItems() {
setInDesktopMode(false)
val hotseatItems =
createHotseatItemsFromPackageNames(listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2))
- .toTypedArray()
- assertThat(taskbarRunningAppsController.updateHotseatItemInfos(hotseatItems))
- .isEqualTo(hotseatItems)
+ assertThat(taskbarRunningAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray()))
+ .isEqualTo(hotseatItems.toTypedArray())
}
@Test
@@ -87,23 +81,22 @@
val runningTasks =
createDesktopTasksFromPackageNames(listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2))
whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
- taskbarRunningAppsController.updateRunningApps(createSparseArray(hotseatItems))
+ taskbarRunningAppsController.updateRunningApps()
val newHotseatItems =
taskbarRunningAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray())
- assertThat(newHotseatItems?.map { it.targetPackage }).isEqualTo(hotseatPackages)
+ assertThat(newHotseatItems.map { it?.targetPackage }).isEqualTo(hotseatPackages)
}
@Test
fun updateHotseatItemInfos_noRunningApps_returnsExistingHotseatItems() {
setInDesktopMode(true)
- val hotseatItems: Array<ItemInfo> =
+ val hotseatItems =
createHotseatItemsFromPackageNames(listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2))
- .toTypedArray()
- assertThat(taskbarRunningAppsController.updateHotseatItemInfos(hotseatItems))
- .isEqualTo(hotseatItems)
+ assertThat(taskbarRunningAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray()))
+ .isEqualTo(hotseatItems.toTypedArray())
}
@Test
@@ -114,7 +107,7 @@
val runningTasks =
createDesktopTasksFromPackageNames(listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2))
whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
- taskbarRunningAppsController.updateRunningApps(createSparseArray(hotseatItems))
+ taskbarRunningAppsController.updateRunningApps()
val newHotseatItems =
taskbarRunningAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray())
@@ -126,7 +119,7 @@
RUNNING_APP_PACKAGE_1,
RUNNING_APP_PACKAGE_2,
)
- assertThat(newHotseatItems?.map { it.targetPackage }).isEqualTo(expectedPackages)
+ assertThat(newHotseatItems.map { it?.targetPackage }).isEqualTo(expectedPackages)
}
@Test
@@ -139,7 +132,7 @@
listOf(HOTSEAT_PACKAGE_1, RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2)
)
whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
- taskbarRunningAppsController.updateRunningApps(createSparseArray(hotseatItems))
+ taskbarRunningAppsController.updateRunningApps()
val newHotseatItems =
taskbarRunningAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray())
@@ -151,7 +144,7 @@
RUNNING_APP_PACKAGE_1,
RUNNING_APP_PACKAGE_2,
)
- assertThat(newHotseatItems?.map { it.targetPackage }).isEqualTo(expectedPackages)
+ assertThat(newHotseatItems.map { it?.targetPackage }).isEqualTo(expectedPackages)
}
@Test
@@ -160,7 +153,7 @@
val runningTasks =
createDesktopTasksFromPackageNames(listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2))
whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
- taskbarRunningAppsController.updateRunningApps(createSparseArray(emptyList()))
+ taskbarRunningAppsController.updateRunningApps()
assertThat(taskbarRunningAppsController.runningApps).isEqualTo(emptySet<String>())
}
@@ -171,7 +164,7 @@
val runningTasks =
createDesktopTasksFromPackageNames(listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2))
whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
- taskbarRunningAppsController.updateRunningApps(createSparseArray(emptyList()))
+ taskbarRunningAppsController.updateRunningApps()
assertThat(taskbarRunningAppsController.runningApps)
.isEqualTo(setOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2))
@@ -204,12 +197,6 @@
whenever(mockDesktopVisibilityController.areDesktopTasksVisible()).thenReturn(inDesktopMode)
}
- private fun createSparseArray(itemInfos: List<ItemInfo>): SparseArray<ItemInfo> {
- val sparseArray = SparseArray<ItemInfo>()
- itemInfos.forEachIndexed { index, itemInfo -> sparseArray[index] = itemInfo }
- return sparseArray
- }
-
private companion object {
const val HOTSEAT_PACKAGE_1 = "hotseat1"
const val HOTSEAT_PACKAGE_2 = "hotseat2"
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 7d1f43f..009d709 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1708,7 +1708,7 @@
AbstractFloatingView.closeAllOpenViews(this);
getStateManager().goToState(ALL_APPS, alreadyOnHome);
if (mAppsView.isSearching()) {
- mAppsView.reset(alreadyOnHome);
+ mAppsView.getSearchUiManager().resetSearch();
}
if (mAppsView.getCurrentPage() != tab) {
mAppsView.switchToTab(tab);
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 8026d4a..2f623e2 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -499,18 +499,15 @@
* Exits search and returns to A-Z apps list. Scroll to the private space header.
*/
public void resetAndScrollToPrivateSpaceHeader() {
- if (mTouchHandler != null) {
- mTouchHandler.endFastScrolling();
- }
-
- // Reset the base recycler view after transitioning home.
- updateHeaderScroll(0);
-
// Animate to A-Z with 0 time to reset the animation with proper state management.
+ // We can't rely on `animateToSearchState` with delay inside `resetSearch` because that will
+ // conflict with following scrolling to bottom, so we need it with 0 time here.
animateToSearchState(false, 0);
MAIN_EXECUTOR.getHandler().post(() -> {
// Reset the search bar after transitioning home.
+ // When `resetSearch` is called after `animateToSearchState` is finished, the inside
+ // `animateToSearchState` with delay is a just no-op and return early.
mSearchUiManager.resetSearch();
// Switch to the main tab
switchToTab(ActivityAllAppsContainerView.AdapterHolder.MAIN);