Merge "Converting ModelTask to an interface instead of an abstract class" into main
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index 5ac5761..9eabb55 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -32,6 +32,7 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.DisplayController;
import com.android.quickstep.GestureState;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.views.DesktopAppSelectView;
@@ -229,6 +230,7 @@
for (DesktopVisibilityListener listener : mDesktopVisibilityListeners) {
listener.onDesktopVisibilityChanged(areDesktopTasksVisible);
}
+ DisplayController.handleInfoChangeForDesktopMode(mLauncher);
}
/**
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/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 2c2311a..2ce6a41 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -24,10 +24,6 @@
import android.animation.Animator;
import android.animation.AnimatorSet;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.TaskTransitionSpec;
-import android.view.WindowManagerGlobal;
import android.window.RemoteTransition;
import androidx.annotation.NonNull;
@@ -37,7 +33,6 @@
import com.android.launcher3.Flags;
import com.android.launcher3.LauncherState;
import com.android.launcher3.QuickstepTransitionManager;
-import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.logging.InstanceId;
@@ -139,7 +134,6 @@
mLauncher.setTaskbarUIController(null);
mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
mHomeState.removeListener(mVisibilityChangeListener);
- updateTaskTransitionSpec(true);
}
private void onInAppDisplayProgressChanged() {
@@ -278,26 +272,6 @@
private void onStashedInAppChanged(DeviceProfile deviceProfile) {
boolean taskbarStashedInApps = mControllers.taskbarStashController.isStashedInApp();
deviceProfile.isTaskbarPresentInApps = !taskbarStashedInApps;
- updateTaskTransitionSpec(taskbarStashedInApps);
- }
-
- private void updateTaskTransitionSpec(boolean taskbarIsHidden) {
- try {
- if (taskbarIsHidden) {
- // Clear custom task transition settings when the taskbar is stashed
- WindowManagerGlobal.getWindowManagerService().clearTaskTransitionSpec();
- } else {
- // Adjust task transition spec to account for taskbar being visible
- WindowManagerGlobal.getWindowManagerService().setTaskTransitionSpec(
- new TaskTransitionSpec(
- mLauncher.getColor(R.color.taskbar_background)));
- }
- } catch (RemoteException e) {
- // This shouldn't happen but if it does task animations won't look good until the
- // taskbar stashing state is changed.
- Log.e(TAG, "Failed to update task transition spec to account for new taskbar state",
- e);
- }
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 2c5aeb3..d5306fb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -317,38 +317,28 @@
mPropertyHolders.add(new StatePropertyHolder(
mControllers.taskbarDragLayerController.getNavbarBackgroundAlpha(),
flags -> (flags & FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE) != 0));
-
- // Rotation button
- RotationButton rotationButton = new RotationButtonImpl(
- addButton(mEndContextualContainer, R.id.rotate_suggestion,
- R.layout.taskbar_contextual_button));
- rotationButton.hide();
- mControllers.rotationButtonController.setRotationButton(rotationButton, null);
- } else {
- mFloatingRotationButton = new FloatingRotationButton(
- ENABLE_TASKBAR_NAVBAR_UNIFICATION ? mNavigationBarPanelContext : mContext,
- R.string.accessibility_rotate_button,
- R.layout.rotate_suggestion,
- R.id.rotate_suggestion,
- R.dimen.floating_rotation_button_min_margin,
- R.dimen.rounded_corner_content_padding,
- R.dimen.floating_rotation_button_taskbar_left_margin,
- R.dimen.floating_rotation_button_taskbar_bottom_margin,
- R.dimen.floating_rotation_button_diameter,
- R.dimen.key_button_ripple_max_width,
- R.bool.floating_rotation_button_position_left);
- mControllers.rotationButtonController.setRotationButton(mFloatingRotationButton,
- mRotationButtonListener);
-
- if (!mIsImeRenderingNavButtons) {
- View imeDownButton = addButton(R.drawable.ic_sysbar_back, BUTTON_BACK,
- mStartContextualContainer, mControllers.navButtonController, R.id.back);
- imeDownButton.setRotation(Utilities.isRtl(resources) ? 90 : -90);
- // Only show when IME is visible.
- mPropertyHolders.add(new StatePropertyHolder(imeDownButton,
- flags -> (flags & FLAG_IME_VISIBLE) != 0));
- }
+ } else if (!mIsImeRenderingNavButtons) {
+ View imeDownButton = addButton(R.drawable.ic_sysbar_back, BUTTON_BACK,
+ mStartContextualContainer, mControllers.navButtonController, R.id.back);
+ imeDownButton.setRotation(Utilities.isRtl(resources) ? 90 : -90);
+ // Only show when IME is visible.
+ mPropertyHolders.add(new StatePropertyHolder(imeDownButton,
+ flags -> (flags & FLAG_IME_VISIBLE) != 0));
}
+ mFloatingRotationButton = new FloatingRotationButton(
+ ENABLE_TASKBAR_NAVBAR_UNIFICATION ? mNavigationBarPanelContext : mContext,
+ R.string.accessibility_rotate_button,
+ R.layout.rotate_suggestion,
+ R.id.rotate_suggestion,
+ R.dimen.floating_rotation_button_min_margin,
+ R.dimen.rounded_corner_content_padding,
+ R.dimen.floating_rotation_button_taskbar_left_margin,
+ R.dimen.floating_rotation_button_taskbar_bottom_margin,
+ R.dimen.floating_rotation_button_diameter,
+ R.dimen.key_button_ripple_max_width,
+ R.bool.floating_rotation_button_position_left);
+ mControllers.rotationButtonController.setRotationButton(mFloatingRotationButton,
+ mRotationButtonListener);
applyState();
mPropertyHolders.forEach(StatePropertyHolder::endAnimation);
@@ -791,7 +781,6 @@
NavButtonLayoutter navButtonLayoutter =
NavButtonLayoutFactory.Companion.getUiLayoutter(
dp, mNavButtonsView, mImeSwitcherButton,
- mControllers.rotationButtonController.getRotationButton(),
mA11yButton, mSpace, res, isInKidsMode, isInSetup, isThreeButtonNav,
mContext.isPhoneMode(), mWindowManagerProxy.getRotation(mContext));
navButtonLayoutter.layoutButtons(mContext, isA11yButtonPersistent());
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 1a94424..8845813 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,25 +298,24 @@
new VoiceInteractionWindowController(this),
new TaskbarTranslationController(this),
new TaskbarSpringOnStashController(this),
- createTaskbarRecentAppsController(isPcMode),
+ createTaskbarRecentAppsController(),
TaskbarEduTooltipController.newInstance(this),
new KeyboardQuickSwitchController(),
- new TaskbarPinningController(this),
+ new TaskbarPinningController(this, () ->
+ DisplayController.INSTANCE.get(this).getInfo().isInDesktopMode()),
bubbleControllersOptional);
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..42e6edb 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;
@@ -26,6 +25,7 @@
import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
import static com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate;
import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
+import static com.android.launcher3.util.DisplayController.CHANGE_DESKTOP_MODE;
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
import static com.android.launcher3.util.DisplayController.CHANGE_TASKBAR_PINNING;
import static com.android.launcher3.util.DisplayController.TASKBAR_NOT_DESTROYED_TAG;
@@ -143,7 +143,7 @@
private class RecreationListener implements DisplayController.DisplayInfoChangeListener {
@Override
public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) {
- if ((flags & (CHANGE_DENSITY | CHANGE_NAVIGATION_MODE
+ if ((flags & (CHANGE_DENSITY | CHANGE_NAVIGATION_MODE | CHANGE_DESKTOP_MODE
| CHANGE_TASKBAR_PINNING)) != 0) {
recreateTaskbar();
}
@@ -426,9 +426,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/TaskbarPinningController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt
index 2f2d636..6c9cc64 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt
@@ -23,6 +23,7 @@
import com.android.app.animation.Interpolators
import com.android.launcher3.LauncherPrefs
import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING
+import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING_IN_DESKTOP_MODE
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_PINNED
@@ -31,8 +32,10 @@
import java.io.PrintWriter
/** Controls taskbar pinning through a popup view. */
-class TaskbarPinningController(private val context: TaskbarActivityContext) :
- TaskbarControllers.LoggableTaskbarController {
+class TaskbarPinningController(
+ private val context: TaskbarActivityContext,
+ private val isInDesktopModeProvider: () -> Boolean,
+) : TaskbarControllers.LoggableTaskbarController {
private lateinit var controllers: TaskbarControllers
private lateinit var taskbarSharedState: TaskbarSharedState
@@ -54,14 +57,22 @@
if (!didPreferenceChange) {
return
}
+ val shouldPinTaskbar =
+ if (isInDesktopModeProvider()) {
+ !launcherPrefs.get(TASKBAR_PINNING_IN_DESKTOP_MODE)
+ } else {
+ !launcherPrefs.get(TASKBAR_PINNING)
+ }
+
val animateToValue =
- if (!launcherPrefs.get(TASKBAR_PINNING)) {
+ if (shouldPinTaskbar) {
statsLogManager.logger().log(LAUNCHER_TASKBAR_PINNED)
PINNING_PERSISTENT
} else {
statsLogManager.logger().log(LAUNCHER_TASKBAR_UNPINNED)
PINNING_TRANSIENT
}
+
taskbarSharedState.taskbarWasPinned = animateToValue == PINNING_TRANSIENT
animateTaskbarPinning(animateToValue)
}
@@ -123,13 +134,24 @@
@VisibleForTesting
fun recreateTaskbarAndUpdatePinningValue() {
updateIsAnimatingTaskbarPinningAndNotifyTaskbarDragLayer(false)
- launcherPrefs.put(TASKBAR_PINNING, !launcherPrefs.get(TASKBAR_PINNING))
+ if (isInDesktopModeProvider()) {
+ launcherPrefs.put(
+ TASKBAR_PINNING_IN_DESKTOP_MODE,
+ !launcherPrefs.get(TASKBAR_PINNING_IN_DESKTOP_MODE)
+ )
+ } else {
+ launcherPrefs.put(TASKBAR_PINNING, !launcherPrefs.get(TASKBAR_PINNING))
+ }
}
override fun dumpLogs(prefix: String, pw: PrintWriter) {
pw.println(prefix + "TaskbarPinningController:")
pw.println("$prefix\tisAnimatingTaskbarPinning=$isAnimatingTaskbarPinning")
pw.println("$prefix\tTASKBAR_PINNING shared pref =" + launcherPrefs.get(TASKBAR_PINNING))
+ pw.println(
+ "$prefix\tTASKBAR_PINNING_IN_DESKTOP_MODE shared pref in desktop mode =" +
+ launcherPrefs.get(TASKBAR_PINNING_IN_DESKTOP_MODE)
+ )
}
companion object {
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/BubbleBarPinController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarPinController.kt
index 8ed9949..9e5ffc9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarPinController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarPinController.kt
@@ -19,7 +19,6 @@
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Point
-import android.graphics.RectF
import android.view.Gravity.BOTTOM
import android.view.Gravity.LEFT
import android.view.Gravity.RIGHT
@@ -37,44 +36,29 @@
class BubbleBarPinController(
private val context: Context,
private val container: FrameLayout,
- private val screenSizeProvider: () -> Point
-) : BaseBubblePinController() {
+ screenSizeProvider: () -> Point
+) : BaseBubblePinController(screenSizeProvider) {
private lateinit var bubbleBarViewController: BubbleBarViewController
private lateinit var bubbleStashController: BubbleStashController
+ private var exclRectWidth: Float = 0f
+ private var exclRectHeight: Float = 0f
+
private var dropTargetView: View? = null
fun init(bubbleControllers: BubbleControllers) {
bubbleBarViewController = bubbleControllers.bubbleBarViewController
bubbleStashController = bubbleControllers.bubbleStashController
+ exclRectWidth = context.resources.getDimension(R.dimen.bubblebar_dismiss_zone_width)
+ exclRectHeight = context.resources.getDimension(R.dimen.bubblebar_dismiss_zone_height)
}
- override fun getScreenCenterX(): Int {
- return screenSizeProvider.invoke().x / 2
+ override fun getExclusionRectWidth(): Float {
+ return exclRectWidth
}
- override fun getExclusionRect(): RectF {
- val rect =
- RectF(
- 0f,
- 0f,
- context.resources.getDimension(R.dimen.bubblebar_dismiss_zone_width),
- context.resources.getDimension(R.dimen.bubblebar_dismiss_zone_height)
- )
- val screenSize = screenSizeProvider.invoke()
- val middleX = screenSize.x / 2
- // Center it around the bottom center of the screen
- rect.offsetTo(middleX - rect.width() / 2, screenSize.y - rect.height())
- return rect
- }
-
- override fun createDropTargetView(): View {
- return LayoutInflater.from(context)
- .inflate(R.layout.bubble_bar_drop_target, container, false)
- .also { view ->
- dropTargetView = view
- container.addView(view)
- }
+ override fun getExclusionRectHeight(): Float {
+ return exclRectHeight
}
override fun getDropTargetView(): View? {
@@ -86,6 +70,15 @@
dropTargetView = null
}
+ override fun createDropTargetView(): View {
+ return LayoutInflater.from(context)
+ .inflate(R.layout.bubble_bar_drop_target, container, false)
+ .also { view ->
+ dropTargetView = view
+ container.addView(view)
+ }
+ }
+
@SuppressLint("RtlHardcoded")
override fun updateLocation(location: BubbleBarLocation) {
val onLeft = location.isOnLeft(container.isLayoutRtl)
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/taskbar/navbutton/AbstractNavButtonLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
index fe91362..8ad2493 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
@@ -27,7 +27,6 @@
import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter
-import com.android.systemui.shared.rotation.RotationButton
/**
* Meant to be a simple container for data subclasses will need
@@ -41,14 +40,13 @@
* @property startContextualContainer ViewGroup that holds the start contextual button (ex, A11y).
*/
abstract class AbstractNavButtonLayoutter(
- val resources: Resources,
- val navButtonContainer: LinearLayout,
- protected val endContextualContainer: ViewGroup,
- protected val startContextualContainer: ViewGroup,
- protected val imeSwitcher: ImageView?,
- protected val rotationButton: RotationButton?,
- protected val a11yButton: ImageView?,
- protected val space: Space?
+ val resources: Resources,
+ val navButtonContainer: LinearLayout,
+ protected val endContextualContainer: ViewGroup,
+ protected val startContextualContainer: ViewGroup,
+ protected val imeSwitcher: ImageView?,
+ protected val a11yButton: ImageView?,
+ protected val space: Space?
) : NavButtonLayoutter {
protected val homeButton: ImageView? = navButtonContainer.findViewById(R.id.home)
protected val recentsButton: ImageView? = navButtonContainer.findViewById(R.id.recent_apps)
@@ -66,17 +64,24 @@
}
fun getParamsToCenterView(): FrameLayout.LayoutParams {
- val params = FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
+ val params =
+ FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
params.gravity = Gravity.CENTER
- return params;
+ return params
}
- open fun repositionContextualContainer(contextualContainer: ViewGroup, buttonSize: Int,
- barAxisMarginStart: Int, barAxisMarginEnd: Int,
- gravity: Int) {
- val contextualContainerParams = FrameLayout.LayoutParams(
- buttonSize, ViewGroup.LayoutParams.MATCH_PARENT)
+ open fun repositionContextualContainer(
+ contextualContainer: ViewGroup,
+ buttonSize: Int,
+ barAxisMarginStart: Int,
+ barAxisMarginEnd: Int,
+ gravity: Int
+ ) {
+ val contextualContainerParams =
+ FrameLayout.LayoutParams(buttonSize, ViewGroup.LayoutParams.MATCH_PARENT)
contextualContainerParams.apply {
marginStart = barAxisMarginStart
marginEnd = barAxisMarginEnd
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
index 4368b95..aa8f5a3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
@@ -29,27 +29,24 @@
import com.android.launcher3.R
import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.*
-import com.android.systemui.shared.rotation.RotationButton
class KidsNavLayoutter(
- resources: Resources,
- navBarContainer: LinearLayout,
- endContextualContainer: ViewGroup,
- startContextualContainer: ViewGroup,
- imeSwitcher: ImageView?,
- rotationButton: RotationButton?,
- a11yButton: ImageView?,
- space: Space?
+ resources: Resources,
+ navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup,
+ imeSwitcher: ImageView?,
+ a11yButton: ImageView?,
+ space: Space?
) :
AbstractNavButtonLayoutter(
- resources,
- navBarContainer,
- endContextualContainer,
- startContextualContainer,
- imeSwitcher,
- rotationButton,
- a11yButton,
- space
+ resources,
+ navBarContainer,
+ endContextualContainer,
+ startContextualContainer,
+ imeSwitcher,
+ a11yButton,
+ space
) {
override fun layoutButtons(context: TaskbarActivityContext, isA11yButtonPersistent: Boolean) {
@@ -105,11 +102,16 @@
endContextualContainer.removeAllViews()
startContextualContainer.removeAllViews()
- val contextualMargin = resources.getDimensionPixelSize(
- R.dimen.taskbar_contextual_button_padding)
+ val contextualMargin =
+ resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_padding)
repositionContextualContainer(endContextualContainer, WRAP_CONTENT, 0, 0, Gravity.END)
- repositionContextualContainer(startContextualContainer, WRAP_CONTENT, contextualMargin,
- contextualMargin, Gravity.START)
+ repositionContextualContainer(
+ startContextualContainer,
+ WRAP_CONTENT,
+ contextualMargin,
+ contextualMargin,
+ Gravity.START
+ )
if (imeSwitcher != null) {
startContextualContainer.addView(imeSwitcher)
@@ -119,9 +121,5 @@
endContextualContainer.addView(a11yButton)
a11yButton.layoutParams = getParamsToCenterView()
}
- if (rotationButton != null) {
- endContextualContainer.addView(rotationButton.currentView)
- rotationButton.currentView.layoutParams = getParamsToCenterView()
- }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
index 5bfdce9..1e9f09b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
@@ -25,10 +25,11 @@
import android.widget.Space
import com.android.launcher3.DeviceProfile
import com.android.launcher3.taskbar.TaskbarActivityContext
-import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.*
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_END_CONTEXTUAL_BUTTONS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_END_NAV_BUTTONS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_START_CONTEXTUAL_BUTTONS
import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.Companion
import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter
-import com.android.systemui.shared.rotation.RotationButton
/**
* Select the correct layout for nav buttons
@@ -58,7 +59,6 @@
deviceProfile: DeviceProfile,
navButtonsView: NearestTouchFrame,
imeSwitcher: ImageView?,
- rotationButton: RotationButton?,
a11yButton: ImageView?,
space: Space?,
resources: Resources,
@@ -86,7 +86,6 @@
endContextualContainer,
startContextualContainer,
imeSwitcher,
- rotationButton,
a11yButton,
space
)
@@ -98,7 +97,6 @@
endContextualContainer,
startContextualContainer,
imeSwitcher,
- rotationButton,
a11yButton,
space
)
@@ -110,7 +108,6 @@
endContextualContainer,
startContextualContainer,
imeSwitcher,
- rotationButton,
a11yButton,
space
)
@@ -123,7 +120,6 @@
endContextualContainer,
startContextualContainer,
imeSwitcher,
- rotationButton,
a11yButton,
space
)
@@ -138,7 +134,6 @@
endContextualContainer,
startContextualContainer,
imeSwitcher,
- rotationButton,
a11yButton,
space
)
@@ -150,7 +145,6 @@
endContextualContainer,
startContextualContainer,
imeSwitcher,
- rotationButton,
a11yButton,
space
)
@@ -162,7 +156,6 @@
endContextualContainer,
startContextualContainer,
imeSwitcher,
- rotationButton,
a11yButton,
space
)
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
index bf820c0..8d91f2c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
@@ -22,29 +22,26 @@
import android.widget.LinearLayout
import android.widget.Space
import com.android.launcher3.taskbar.TaskbarActivityContext
-import com.android.systemui.shared.rotation.RotationButton
/** Layoutter for showing gesture navigation on phone screen. No buttons here, no-op container */
class PhoneGestureLayoutter(
- resources: Resources,
- navBarContainer: LinearLayout,
- endContextualContainer: ViewGroup,
- startContextualContainer: ViewGroup,
- imeSwitcher: ImageView?,
- rotationButton: RotationButton?,
- a11yButton: ImageView?,
- space: Space?
+ resources: Resources,
+ navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup,
+ imeSwitcher: ImageView?,
+ a11yButton: ImageView?,
+ space: Space?
) :
- AbstractNavButtonLayoutter(
- resources,
- navBarContainer,
- endContextualContainer,
- startContextualContainer,
- imeSwitcher,
- rotationButton,
- a11yButton,
- space
- ) {
+ AbstractNavButtonLayoutter(
+ resources,
+ navBarContainer,
+ endContextualContainer,
+ startContextualContainer,
+ imeSwitcher,
+ a11yButton,
+ space
+ ) {
override fun layoutButtons(context: TaskbarActivityContext, isA11yButtonPersistent: Boolean) {
endContextualContainer.removeAllViews()
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
index 6a935f1..9f7f07e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
@@ -26,35 +26,32 @@
import android.widget.Space
import com.android.launcher3.R
import com.android.launcher3.taskbar.TaskbarActivityContext
-import com.android.systemui.shared.rotation.RotationButton
open class PhoneLandscapeNavLayoutter(
- resources: Resources,
- navBarContainer: LinearLayout,
- endContextualContainer: ViewGroup,
- startContextualContainer: ViewGroup,
- imeSwitcher: ImageView?,
- rotationButton: RotationButton?,
- a11yButton: ImageView?,
- space: Space?
+ resources: Resources,
+ navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup,
+ imeSwitcher: ImageView?,
+ a11yButton: ImageView?,
+ space: Space?
) :
AbstractNavButtonLayoutter(
- resources,
- navBarContainer,
- endContextualContainer,
- startContextualContainer,
- imeSwitcher,
- rotationButton,
- a11yButton,
- space
+ resources,
+ navBarContainer,
+ endContextualContainer,
+ startContextualContainer,
+ imeSwitcher,
+ a11yButton,
+ space
) {
override fun layoutButtons(context: TaskbarActivityContext, isA11yButtonPersistent: Boolean) {
val totalHeight = context.deviceProfile.heightPx
- val homeButtonHeight = resources.getDimensionPixelSize(
- R.dimen.taskbar_phone_home_button_size)
- val roundedCornerContentMargin = resources.getDimensionPixelSize(
- R.dimen.taskbar_phone_rounded_corner_content_margin)
+ val homeButtonHeight =
+ resources.getDimensionPixelSize(R.dimen.taskbar_phone_home_button_size)
+ val roundedCornerContentMargin =
+ resources.getDimensionPixelSize(R.dimen.taskbar_phone_rounded_corner_content_margin)
val contentPadding = resources.getDimensionPixelSize(R.dimen.taskbar_phone_content_padding)
val contentWidth = totalHeight - roundedCornerContentMargin * 2 - contentPadding * 2
@@ -63,13 +60,13 @@
val sideButtonHeight = contextualButtonHeight * 2
val navButtonContainerHeight = contentWidth - contextualButtonHeight * 2
- val navContainerParams = FrameLayout.LayoutParams(
- MATCH_PARENT, navButtonContainerHeight.toInt())
+ val navContainerParams =
+ FrameLayout.LayoutParams(MATCH_PARENT, navButtonContainerHeight.toInt())
navContainerParams.apply {
topMargin =
- (contextualButtonHeight + contentPadding + roundedCornerContentMargin).toInt()
+ (contextualButtonHeight + contentPadding + roundedCornerContentMargin).toInt()
bottomMargin =
- (contextualButtonHeight + contentPadding + roundedCornerContentMargin).toInt()
+ (contextualButtonHeight + contentPadding + roundedCornerContentMargin).toInt()
marginEnd = 0
marginStart = 0
}
@@ -84,8 +81,8 @@
navButtonContainer.gravity = Gravity.CENTER
// Add the spaces in between the nav buttons
- val spaceInBetween = (navButtonContainerHeight - homeButtonHeight -
- sideButtonHeight * 2) / 2.0f
+ val spaceInBetween =
+ (navButtonContainerHeight - homeButtonHeight - sideButtonHeight * 2) / 2.0f
for (i in 0 until navButtonContainer.childCount) {
val navButton = navButtonContainer.getChildAt(i)
val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams
@@ -124,13 +121,23 @@
endContextualContainer.removeAllViews()
startContextualContainer.removeAllViews()
- val roundedCornerContentMargin = resources.getDimensionPixelSize(
- R.dimen.taskbar_phone_rounded_corner_content_margin)
+ val roundedCornerContentMargin =
+ resources.getDimensionPixelSize(R.dimen.taskbar_phone_rounded_corner_content_margin)
val contentPadding = resources.getDimensionPixelSize(R.dimen.taskbar_phone_content_padding)
- repositionContextualContainer(startContextualContainer, buttonSize,
- roundedCornerContentMargin + contentPadding, 0, Gravity.TOP)
- repositionContextualContainer(endContextualContainer, buttonSize,
- 0, roundedCornerContentMargin + contentPadding, Gravity.BOTTOM)
+ repositionContextualContainer(
+ startContextualContainer,
+ buttonSize,
+ roundedCornerContentMargin + contentPadding,
+ 0,
+ Gravity.TOP
+ )
+ repositionContextualContainer(
+ endContextualContainer,
+ buttonSize,
+ 0,
+ roundedCornerContentMargin + contentPadding,
+ Gravity.BOTTOM
+ )
if (imeSwitcher != null) {
startContextualContainer.addView(imeSwitcher)
@@ -140,16 +147,16 @@
startContextualContainer.addView(a11yButton)
a11yButton.layoutParams = getParamsToCenterView()
}
- if (rotationButton != null) {
- startContextualContainer.addView(rotationButton.currentView)
- rotationButton.currentView.layoutParams = getParamsToCenterView()
- }
endContextualContainer.addView(space, MATCH_PARENT, MATCH_PARENT)
}
- override fun repositionContextualContainer(contextualContainer: ViewGroup, buttonSize: Int,
- barAxisMarginTop: Int, barAxisMarginBottom: Int,
- gravity: Int) {
+ override fun repositionContextualContainer(
+ contextualContainer: ViewGroup,
+ buttonSize: Int,
+ barAxisMarginTop: Int,
+ barAxisMarginBottom: Int,
+ gravity: Int
+ ) {
val contextualContainerParams = FrameLayout.LayoutParams(MATCH_PARENT, buttonSize)
contextualContainerParams.apply {
marginStart = 0
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
index 0672270..5b24ebf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
@@ -26,34 +26,32 @@
import android.widget.Space
import com.android.launcher3.R
import com.android.launcher3.taskbar.TaskbarActivityContext
-import com.android.systemui.shared.rotation.RotationButton
class PhonePortraitNavLayoutter(
- resources: Resources,
- navBarContainer: LinearLayout,
- endContextualContainer: ViewGroup,
- startContextualContainer: ViewGroup,
- imeSwitcher: ImageView?,
- rotationButton: RotationButton?,
- a11yButton: ImageView?,
- space: Space?
+ resources: Resources,
+ navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup,
+ imeSwitcher: ImageView?,
+ a11yButton: ImageView?,
+ space: Space?
) :
AbstractNavButtonLayoutter(
- resources,
- navBarContainer,
- endContextualContainer,
- startContextualContainer,
- imeSwitcher,
- rotationButton,
- a11yButton,
- space
+ resources,
+ navBarContainer,
+ endContextualContainer,
+ startContextualContainer,
+ imeSwitcher,
+ a11yButton,
+ space
) {
override fun layoutButtons(context: TaskbarActivityContext, isA11yButtonPersistent: Boolean) {
val totalWidth = context.deviceProfile.widthPx
- val homeButtonWidth = resources.getDimensionPixelSize(R.dimen.taskbar_phone_home_button_size)
- val roundedCornerContentMargin = resources.getDimensionPixelSize(
- R.dimen.taskbar_phone_rounded_corner_content_margin)
+ val homeButtonWidth =
+ resources.getDimensionPixelSize(R.dimen.taskbar_phone_home_button_size)
+ val roundedCornerContentMargin =
+ resources.getDimensionPixelSize(R.dimen.taskbar_phone_rounded_corner_content_margin)
val contentPadding = resources.getDimensionPixelSize(R.dimen.taskbar_phone_content_padding)
val contentWidth = totalWidth - roundedCornerContentMargin * 2 - contentPadding * 2
@@ -62,15 +60,18 @@
val sideButtonWidth = contextualButtonWidth * 2
val navButtonContainerWidth = contentWidth - contextualButtonWidth * 2
- val navContainerParams = FrameLayout.LayoutParams(navButtonContainerWidth.toInt(),
- ViewGroup.LayoutParams.MATCH_PARENT)
+ val navContainerParams =
+ FrameLayout.LayoutParams(
+ navButtonContainerWidth.toInt(),
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
navContainerParams.apply {
topMargin = 0
bottomMargin = 0
marginEnd =
- (contextualButtonWidth + contentPadding + roundedCornerContentMargin).toInt()
+ (contextualButtonWidth + contentPadding + roundedCornerContentMargin).toInt()
marginStart =
- (contextualButtonWidth + contentPadding + roundedCornerContentMargin).toInt()
+ (contextualButtonWidth + contentPadding + roundedCornerContentMargin).toInt()
}
// Ensure order of buttons is correct
@@ -85,8 +86,8 @@
navButtonContainer.gravity = Gravity.CENTER
// Add the spaces in between the nav buttons
- val spaceInBetween = (navButtonContainerWidth - homeButtonWidth -
- sideButtonWidth * 2) / 2.0f
+ val spaceInBetween =
+ (navButtonContainerWidth - homeButtonWidth - sideButtonWidth * 2) / 2.0f
for (i in 0 until navButtonContainer.childCount) {
val navButton = navButtonContainer.getChildAt(i)
val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams
@@ -114,10 +115,20 @@
endContextualContainer.removeAllViews()
startContextualContainer.removeAllViews()
- repositionContextualContainer(startContextualContainer, contextualButtonWidth.toInt(),
- roundedCornerContentMargin + contentPadding, 0, Gravity.START)
- repositionContextualContainer(endContextualContainer, contextualButtonWidth.toInt(), 0,
- roundedCornerContentMargin + contentPadding, Gravity.END)
+ repositionContextualContainer(
+ startContextualContainer,
+ contextualButtonWidth.toInt(),
+ roundedCornerContentMargin + contentPadding,
+ 0,
+ Gravity.START
+ )
+ repositionContextualContainer(
+ endContextualContainer,
+ contextualButtonWidth.toInt(),
+ 0,
+ roundedCornerContentMargin + contentPadding,
+ Gravity.END
+ )
startContextualContainer.addView(space, MATCH_PARENT, MATCH_PARENT)
if (imeSwitcher != null) {
@@ -128,9 +139,5 @@
endContextualContainer.addView(a11yButton)
a11yButton.layoutParams = getParamsToCenterView()
}
- if (rotationButton != null) {
- endContextualContainer.addView(rotationButton.currentView)
- rotationButton.currentView.layoutParams = getParamsToCenterView()
- }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt
index 869cc43..f0b47f4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt
@@ -1,18 +1,18 @@
/*
-* Copyright (C) 2023 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License
-*/
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
package com.android.launcher3.taskbar.navbutton
@@ -24,28 +24,25 @@
import android.widget.LinearLayout
import android.widget.Space
import com.android.launcher3.R
-import com.android.systemui.shared.rotation.RotationButton
class PhoneSeascapeNavLayoutter(
- resources: Resources,
- navBarContainer: LinearLayout,
- endContextualContainer: ViewGroup,
- startContextualContainer: ViewGroup,
- imeSwitcher: ImageView?,
- rotationButton: RotationButton?,
- a11yButton: ImageView?,
- space: Space?
+ resources: Resources,
+ navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup,
+ imeSwitcher: ImageView?,
+ a11yButton: ImageView?,
+ space: Space?
) :
- PhoneLandscapeNavLayoutter(
- resources,
- navBarContainer,
- endContextualContainer,
- startContextualContainer,
- imeSwitcher,
- rotationButton,
- a11yButton,
- space
- ) {
+ PhoneLandscapeNavLayoutter(
+ resources,
+ navBarContainer,
+ endContextualContainer,
+ startContextualContainer,
+ imeSwitcher,
+ a11yButton,
+ space
+ ) {
override fun addThreeButtons() {
// Flip ordering of back and recents buttons
@@ -58,13 +55,23 @@
endContextualContainer.removeAllViews()
startContextualContainer.removeAllViews()
- val roundedCornerContentMargin = resources.getDimensionPixelSize(
- R.dimen.taskbar_phone_rounded_corner_content_margin)
+ val roundedCornerContentMargin =
+ resources.getDimensionPixelSize(R.dimen.taskbar_phone_rounded_corner_content_margin)
val contentPadding = resources.getDimensionPixelSize(R.dimen.taskbar_phone_content_padding)
- repositionContextualContainer(startContextualContainer, buttonSize,
- roundedCornerContentMargin + contentPadding, 0, Gravity.TOP)
- repositionContextualContainer(endContextualContainer, buttonSize, 0,
- roundedCornerContentMargin + contentPadding, Gravity.BOTTOM)
+ repositionContextualContainer(
+ startContextualContainer,
+ buttonSize,
+ roundedCornerContentMargin + contentPadding,
+ 0,
+ Gravity.TOP
+ )
+ repositionContextualContainer(
+ endContextualContainer,
+ buttonSize,
+ 0,
+ roundedCornerContentMargin + contentPadding,
+ Gravity.BOTTOM
+ )
startContextualContainer.addView(space, MATCH_PARENT, MATCH_PARENT)
if (imeSwitcher != null) {
@@ -75,9 +82,5 @@
endContextualContainer.addView(a11yButton)
a11yButton.layoutParams = getParamsToCenterView()
}
- if (rotationButton != null) {
- endContextualContainer.addView(rotationButton.currentView)
- rotationButton.currentView.layoutParams = getParamsToCenterView()
- }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
index 8eff95c..91042c3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
@@ -27,7 +27,6 @@
import com.android.launcher3.DeviceProfile
import com.android.launcher3.R
import com.android.launcher3.taskbar.TaskbarActivityContext
-import com.android.systemui.shared.rotation.RotationButton
const val SQUARE_ASPECT_RATIO_BOTTOM_BOUND = 0.95
const val SQUARE_ASPECT_RATIO_UPPER_BOUND = 1.05
@@ -39,7 +38,6 @@
endContextualContainer: ViewGroup,
startContextualContainer: ViewGroup,
imeSwitcher: ImageView?,
- rotationButton: RotationButton?,
a11yButton: ImageView?,
space: Space?
) :
@@ -49,7 +47,6 @@
endContextualContainer,
startContextualContainer,
imeSwitcher,
- rotationButton,
a11yButton,
space
) {
@@ -116,9 +113,5 @@
endContextualContainer.addView(a11yButton)
a11yButton.layoutParams = getParamsToCenterView()
}
- if (rotationButton != null) {
- endContextualContainer.addView(rotationButton.currentView)
- rotationButton.currentView.layoutParams = getParamsToCenterView()
- }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
index 34d3fad..a59e8a8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
@@ -26,7 +26,6 @@
import android.widget.Space
import com.android.launcher3.R
import com.android.launcher3.taskbar.TaskbarActivityContext
-import com.android.systemui.shared.rotation.RotationButton
/** Layoutter for rendering task bar in large screen, both in 3-button and gesture nav mode. */
class TaskbarNavLayoutter(
@@ -35,7 +34,6 @@
endContextualContainer: ViewGroup,
startContextualContainer: ViewGroup,
imeSwitcher: ImageView?,
- rotationButton: RotationButton?,
a11yButton: ImageView?,
space: Space?
) :
@@ -45,7 +43,6 @@
endContextualContainer,
startContextualContainer,
imeSwitcher,
- rotationButton,
a11yButton,
space
) {
@@ -137,10 +134,6 @@
endContextualContainer.addView(a11yButton)
a11yButton.layoutParams = getParamsToCenterView()
}
- if (rotationButton != null) {
- endContextualContainer.addView(rotationButton.currentView)
- rotationButton.currentView.layoutParams = getParamsToCenterView()
- }
}
}
}
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/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index c8a91df..81c9d4a 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -186,6 +186,7 @@
private final long mSwipeUpStartTimeMs = SystemClock.uptimeMillis();
private boolean mHandlingAtomicEvent;
+ private boolean mIsInExtendedSlopRegion;
public GestureState(OverviewComponentObserver componentObserver, int gestureId) {
mHomeIntent = componentObserver.getHomeIntent();
@@ -494,6 +495,25 @@
}
/**
+ * Set whether it's in long press nav handle (LPNH)'s extended touch slop region, e.g., second
+ * stage region in order to continue respect LPNH and ignore other touch slop logic.
+ * This will only be set to true when flag ENABLE_LPNH_TWO_STAGES is turned on.
+ */
+ public void setIsInExtendedSlopRegion(boolean isInExtendedSlopRegion) {
+ if (DeviceConfigWrapper.get().getEnableLpnhTwoStages()) {
+ mIsInExtendedSlopRegion = isInExtendedSlopRegion;
+ }
+ }
+
+ /**
+ * Returns whether it's in LPNH's extended touch slop region. This is only valid when flag
+ * ENABLE_LPNH_TWO_STAGES is turned on.
+ */
+ public boolean isInExtendedSlopRegion() {
+ return mIsInExtendedSlopRegion;
+ }
+
+ /**
* Returns and clears the canceled animation thumbnail data. This call only returns a value
* while STATE_RECENTS_ANIMATION_CANCELED state is being set, and the caller is responsible for
* calling {@link RecentsAnimationController#cleanupScreenshot()}.
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/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 832f4e1..f94a29c 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -1018,7 +1018,7 @@
.append("TaskbarActivityContext != null, ")
.append("using TaskbarUnstashInputConsumer");
base = new TaskbarUnstashInputConsumer(this, base, mInputMonitorCompat, tac,
- mOverviewCommandHelper);
+ mOverviewCommandHelper, mGestureState);
}
}
if (enableBubblesLongPressNavHandle()) {
@@ -1046,7 +1046,7 @@
}
reasonString.append("using NavHandleLongPressInputConsumer");
base = new NavHandleLongPressInputConsumer(this, base, mInputMonitorCompat,
- mDeviceState, navHandle);
+ mDeviceState, navHandle, mGestureState);
}
if (!enableBubblesLongPressNavHandle()) {
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/inputconsumers/NavHandleLongPressInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
index 075e539..848a43a 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
@@ -31,6 +31,7 @@
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.DeviceConfigWrapper;
+import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.NavHandle;
import com.android.quickstep.RecentsAnimationDeviceState;
@@ -61,13 +62,14 @@
private final NavHandle mNavHandle;
private final StatsLogManager mStatsLogManager;
private final TopTaskTracker mTopTaskTracker;
+ private final GestureState mGestureState;
private MotionEvent mCurrentDownEvent;
private boolean mDeepPressLogged; // Whether deep press has been logged for the current touch.
public NavHandleLongPressInputConsumer(Context context, InputConsumer delegate,
InputMonitorCompat inputMonitor, RecentsAnimationDeviceState deviceState,
- NavHandle navHandle) {
+ NavHandle navHandle, GestureState gestureState) {
super(delegate, inputMonitor);
mScreenWidth = DisplayController.INSTANCE.get(context).getInfo().currentSize.x;
mDeepPressEnabled = DeviceConfigWrapper.get().getEnableLpnhDeepPress();
@@ -82,6 +84,8 @@
mTouchSlopSquaredOriginal = deviceState.getSquaredTouchSlop();
mTouchSlopSquared = mTouchSlopSquaredOriginal;
mOuterTouchSlopSquared = mTouchSlopSquared * (twoStageMultiplier * twoStageMultiplier);
+ mGestureState = gestureState;
+ mGestureState.setIsInExtendedSlopRegion(false);
if (DEBUG_NAV_HANDLE) {
Log.d(TAG, "mLongPressTimeout=" + mLongPressTimeout);
Log.d(TAG, "mOuterLongPressTimeout=" + mOuterLongPressTimeout);
@@ -126,6 +130,7 @@
}
mCurrentDownEvent = MotionEvent.obtain(ev);
mTouchSlopSquared = mTouchSlopSquaredOriginal;
+ mGestureState.setIsInExtendedSlopRegion(false);
mDeepPressLogged = false;
if (isInNavBarHorizontalArea(ev.getRawX())) {
mNavHandleLongPressHandler.onTouchStarted(mNavHandle);
@@ -154,6 +159,7 @@
- (int) (ev.getEventTime() - ev.getDownTime());
MAIN_EXECUTOR.getHandler().postDelayed(mTriggerLongPress, delay);
mTouchSlopSquared = mOuterTouchSlopSquared;
+ mGestureState.setIsInExtendedSlopRegion(true);
if (DEBUG_NAV_HANDLE) {
Log.d(TAG, "Touch in middle region!");
}
@@ -219,6 +225,7 @@
if (DEBUG_NAV_HANDLE) {
Log.d(TAG, "cancelLongPress");
}
+ mGestureState.setIsInExtendedSlopRegion(false);
MAIN_EXECUTOR.getHandler().removeCallbacks(mTriggerLongPress);
mNavHandleLongPressHandler.onTouchFinished(mNavHandle, reason);
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 9f39476..0d450c6 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -284,8 +284,9 @@
float horizontalDist = Math.abs(displacementX);
float upDist = -displacement;
- boolean passedSlop = mGestureState.isTrackpadGesture() || squaredHypot(
- displacementX, displacementY) >= mSquaredTouchSlop;
+ boolean passedSlop = mGestureState.isTrackpadGesture()
+ || (squaredHypot(displacementX, displacementY) >= mSquaredTouchSlop
+ && !mGestureState.isInExtendedSlopRegion());
if (!mPassedSlopOnThisGesture && passedSlop) {
mPassedSlopOnThisGesture = true;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index bb8d1d7..c61f71d 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -48,6 +48,7 @@
private final BaseContainerInterface<?, T> mContainerInterface;
private final BaseDragLayer mTarget;
private final InputMonitorCompat mInputMonitor;
+ private final GestureState mGestureState;
private final int[] mLocationOnScreen = new int[2];
@@ -62,6 +63,7 @@
mInputMonitor = inputMonitor;
mStartingInActivityBounds = startingInActivityBounds;
mContainerInterface = gestureState.getContainerInterface();
+ mGestureState = gestureState;
mTarget = container.getDragLayer();
mTarget.getLocationOnScreen(mLocationOnScreen);
@@ -84,7 +86,10 @@
ev.setEdgeFlags(flags | Utilities.EDGE_NAV_BAR);
}
ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]);
- boolean handled = mTarget.proxyTouchEvent(ev, mStartingInActivityBounds);
+ boolean handled = false;
+ if (mGestureState == null || !mGestureState.isInExtendedSlopRegion()) {
+ handled = mTarget.proxyTouchEvent(ev, mStartingInActivityBounds);
+ }
ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
ev.setEdgeFlags(flags);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
index cd180ba..6b3e6e9 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
@@ -42,6 +42,7 @@
import com.android.launcher3.taskbar.bubbles.BubbleControllers;
import com.android.launcher3.touch.OverScroll;
import com.android.launcher3.util.DisplayController;
+import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.OverviewCommandHelper;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -76,10 +77,11 @@
private final int mStashedTaskbarBottomEdge;
private final @Nullable TransitionCallback mTransitionCallback;
+ private final GestureState mGestureState;
public TaskbarUnstashInputConsumer(Context context, InputConsumer delegate,
InputMonitorCompat inputMonitor, TaskbarActivityContext taskbarActivityContext,
- OverviewCommandHelper overviewCommandHelper) {
+ OverviewCommandHelper overviewCommandHelper, GestureState gestureState) {
super(delegate, inputMonitor);
mTaskbarActivityContext = taskbarActivityContext;
mOverviewCommandHelper = overviewCommandHelper;
@@ -103,6 +105,7 @@
mTransitionCallback = mIsTransientTaskbar
? taskbarActivityContext.getTranslationCallbacks()
: null;
+ mGestureState = gestureState;
}
@Override
@@ -111,6 +114,11 @@
}
@Override
+ public boolean allowInterceptByParent() {
+ return super.allowInterceptByParent() && !mHasPassedTaskbarNavThreshold;
+ }
+
+ @Override
public void onMotionEvent(MotionEvent ev) {
if (mState != STATE_ACTIVE) {
boolean isStashedTaskbarHovered = isMouseEvent(ev)
@@ -173,7 +181,8 @@
boolean passedTaskbarNavThreshold = dY < 0
&& Math.abs(dY) >= mTaskbarNavThreshold;
- if (!mHasPassedTaskbarNavThreshold && passedTaskbarNavThreshold) {
+ if (!mHasPassedTaskbarNavThreshold && passedTaskbarNavThreshold
+ && !mGestureState.isInExtendedSlopRegion()) {
mHasPassedTaskbarNavThreshold = true;
if (mIsInBubbleBarArea && mIsVerticalGestureOverBubbleBar) {
mTaskbarActivityContext.onSwipeToOpenBubblebar();
diff --git a/quickstep/src/com/android/quickstep/util/DesktopTask.java b/quickstep/src/com/android/quickstep/util/DesktopTask.java
index b3f5d82..07f2d68 100644
--- a/quickstep/src/com/android/quickstep/util/DesktopTask.java
+++ b/quickstep/src/com/android/quickstep/util/DesktopTask.java
@@ -56,4 +56,10 @@
public DesktopTask copy() {
return new DesktopTask(tasks);
}
+
+ @Override
+ public String toString() {
+ return "type=" + taskViewType + " tasks=" + tasks;
+ }
+
}
diff --git a/quickstep/src/com/android/quickstep/util/GroupTask.java b/quickstep/src/com/android/quickstep/util/GroupTask.java
index 9c49647..7dd6afc 100644
--- a/quickstep/src/com/android/quickstep/util/GroupTask.java
+++ b/quickstep/src/com/android/quickstep/util/GroupTask.java
@@ -70,4 +70,10 @@
task2 != null ? new Task(task2) : null,
mSplitBounds);
}
+
+ @Override
+ public String toString() {
+ return "type=" + taskViewType + " task1=" + task1 + " task2=" + task2;
+ }
+
}
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index f430d79..ee2c2e1 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -23,6 +23,7 @@
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.content.Context
import android.graphics.Bitmap
@@ -50,6 +51,7 @@
import com.android.launcher3.apppairs.AppPairIcon
import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.logging.StatsLogManager.EventEnum
+import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.statehandlers.DepthController
import com.android.launcher3.statemanager.StateManager
import com.android.launcher3.taskbar.TaskbarActivityContext
@@ -69,6 +71,7 @@
import com.android.quickstep.views.TaskView
import com.android.quickstep.views.TaskView.TaskIdAttributeContainer
import com.android.quickstep.views.TaskViewIcon
+import com.android.wm.shell.shared.TransitionUtil
import java.util.Optional
import java.util.function.Supplier
@@ -553,8 +556,14 @@
check(info != null && t != null) {
"trying to launch an app pair icon, but encountered an unexpected null"
}
-
- composeIconSplitLaunchAnimator(launchingIconView, info, t, finishCallback)
+ val appPairLaunchingAppIndex = hasChangesForBothAppPairs(launchingIconView, info)
+ if (appPairLaunchingAppIndex == -1) {
+ // Launch split app pair animation
+ composeIconSplitLaunchAnimator(launchingIconView, info, t, finishCallback)
+ } else {
+ composeFullscreenIconSplitLaunchAnimator(launchingIconView, info, t,
+ finishCallback, appPairLaunchingAppIndex)
+ }
} else {
// Fallback case: simple fade-in animation
check(info != null && t != null) {
@@ -619,6 +628,39 @@
}
/**
+ * @return -1 if [transitionInfo] contains both apps of the app pair to be animated, otherwise
+ * the integer index corresponding to [launchingIconView]'s contents for the single app
+ * to be animated
+ */
+ fun hasChangesForBothAppPairs(launchingIconView: AppPairIcon,
+ transitionInfo: TransitionInfo) : Int {
+ val intent1 = launchingIconView.info.getFirstApp().intent.component?.packageName
+ val intent2 = launchingIconView.info.getSecondApp().intent.component?.packageName
+ var launchFullscreenAppIndex = -1
+ for (change in transitionInfo.changes) {
+ val taskInfo: RunningTaskInfo = change.taskInfo ?: continue
+ if (TransitionUtil.isOpeningType(change.mode) &&
+ taskInfo.windowingMode == WINDOWING_MODE_FULLSCREEN) {
+ val baseIntent = taskInfo.baseIntent.component?.packageName
+ if (baseIntent == intent1) {
+ if (launchFullscreenAppIndex > -1) {
+ launchFullscreenAppIndex = -1
+ break
+ }
+ launchFullscreenAppIndex = 0
+ } else if (baseIntent == intent2) {
+ if (launchFullscreenAppIndex > -1) {
+ launchFullscreenAppIndex = -1
+ break
+ }
+ launchFullscreenAppIndex = 1
+ }
+ }
+ }
+ return launchFullscreenAppIndex
+ }
+
+ /**
* When the user taps an app pair icon to launch split, this will play the tasks' launch
* animation from the position of the icon.
*
@@ -653,7 +695,8 @@
// If launching an app pair from Taskbar inside of an app context (no access to Launcher),
// use the scale-up animation
if (launchingIconView.context is TaskbarActivityContext) {
- composeScaleUpLaunchAnimation(transitionInfo, t, finishCallback)
+ composeScaleUpLaunchAnimation(transitionInfo, t, finishCallback,
+ WINDOWING_MODE_MULTI_WINDOW)
return
}
@@ -663,11 +706,6 @@
// Create an AnimatorSet that will run both shell and launcher transitions together
val launchAnimation = AnimatorSet()
- val progressUpdater = ValueAnimator.ofFloat(0f, 1f)
- val timings = AnimUtils.getDeviceAppPairLaunchTimings(dp.isTablet)
- progressUpdater.setDuration(timings.getDuration().toLong())
- progressUpdater.interpolator = Interpolators.LINEAR
-
var rootCandidate: Change? = null
for (change in transitionInfo.changes) {
@@ -711,27 +749,13 @@
// Make sure nothing weird happened, like getChange() returning null.
check(rootCandidate != null) { "Failed to find a root leash" }
- // Shell animation: the apps are revealed toward end of the launch animation
- progressUpdater.addUpdateListener { valueAnimator: ValueAnimator ->
- val progress =
- Interpolators.clampToProgress(
- Interpolators.LINEAR,
- valueAnimator.animatedFraction,
- timings.appRevealStartOffset,
- timings.appRevealEndOffset
- )
-
- // Set the alpha of the shell layer (2 apps + divider)
- t.setAlpha(rootCandidate.leash, progress)
- t.apply()
- }
-
// Create a new floating view in Launcher, positioned above the launching icon
val drawableArea = launchingIconView.iconDrawableArea
val appIcon1 = launchingIconView.info.getFirstApp().newIcon(launchingIconView.context)
val appIcon2 = launchingIconView.info.getSecondApp().newIcon(launchingIconView.context)
appIcon1.setBounds(0, 0, dp.iconSizePx, dp.iconSizePx)
appIcon2.setBounds(0, 0, dp.iconSizePx, dp.iconSizePx)
+
val floatingView =
FloatingAppPairView.getFloatingAppPairView(
launcher,
@@ -742,84 +766,189 @@
)
floatingView.bringToFront()
- // Launcher animation: animate the floating view, expanding to fill the display surface
- progressUpdater.addUpdateListener(
- object : MultiValueUpdateListener() {
- var mDx =
- FloatProp(
- floatingView.startingPosition.left,
- dp.widthPx / 2f - floatingView.startingPosition.width() / 2f,
- Interpolators.clampToProgress(
- timings.getStagedRectXInterpolator(),
- timings.stagedRectSlideStartOffset,
- timings.stagedRectSlideEndOffset
- )
- )
- var mDy =
- FloatProp(
- floatingView.startingPosition.top,
- dp.heightPx / 2f - floatingView.startingPosition.height() / 2f,
- Interpolators.clampToProgress(
- Interpolators.EMPHASIZED,
- timings.stagedRectSlideStartOffset,
- timings.stagedRectSlideEndOffset
- )
- )
- var mScaleX =
- FloatProp(
- 1f /* start */,
- dp.widthPx / floatingView.startingPosition.width(),
- Interpolators.clampToProgress(
- Interpolators.EMPHASIZED,
- timings.stagedRectSlideStartOffset,
- timings.stagedRectSlideEndOffset
- )
- )
- var mScaleY =
- FloatProp(
- 1f /* start */,
- dp.heightPx / floatingView.startingPosition.height(),
- Interpolators.clampToProgress(
- Interpolators.EMPHASIZED,
- timings.stagedRectSlideStartOffset,
- timings.stagedRectSlideEndOffset
- )
- )
-
- override fun onUpdate(percent: Float, initOnly: Boolean) {
- floatingView.progress = percent
- floatingView.x = mDx.value
- floatingView.y = mDy.value
- floatingView.scaleX = mScaleX.value
- floatingView.scaleY = mScaleY.value
- floatingView.invalidate()
- }
- }
- )
-
- // When animation ends, remove the floating view and run finishCallback
- progressUpdater.addListener(
- object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- safeRemoveViewFromDragLayer(launcher, floatingView)
- finishCallback.run()
- }
- }
- )
-
- launchAnimation.play(progressUpdater)
+ launchAnimation.play(
+ getIconLaunchValueAnimator(t, dp, finishCallback, launcher, floatingView,
+ rootCandidate))
launchAnimation.start()
}
/**
+ * Similar to [composeIconSplitLaunchAnimator], but instructs [FloatingAppPairView] to animate
+ * a single fullscreen icon + background instead of for a pair
+ */
+ @VisibleForTesting
+ fun composeFullscreenIconSplitLaunchAnimator(
+ launchingIconView: AppPairIcon,
+ transitionInfo: TransitionInfo,
+ t: Transaction,
+ finishCallback: Runnable,
+ launchFullscreenIndex: Int
+ ) {
+ // If launching an app pair from Taskbar inside of an app context (no access to Launcher),
+ // use the scale-up animation
+ if (launchingIconView.context is TaskbarActivityContext) {
+ composeScaleUpLaunchAnimation(transitionInfo, t, finishCallback,
+ WINDOWING_MODE_FULLSCREEN)
+ return
+ }
+
+ // Else we are in Launcher and can launch with the full icon stretch-and-split animation.
+ val launcher = QuickstepLauncher.getLauncher(launchingIconView.context)
+ val dp = launcher.deviceProfile
+
+ // Create an AnimatorSet that will run both shell and launcher transitions together
+ val launchAnimation = AnimatorSet()
+
+ val appInfo = launchingIconView.info
+ .getContents()[launchFullscreenIndex] as WorkspaceItemInfo
+ val intentToLaunch = appInfo.intent.component?.packageName
+ var rootCandidate: Change? = null
+ for (change in transitionInfo.changes) {
+ val taskInfo: RunningTaskInfo = change.taskInfo ?: continue
+ val baseIntent = taskInfo.baseIntent.component?.packageName
+ if (TransitionUtil.isOpeningType(change.mode) &&
+ taskInfo.windowingMode == WINDOWING_MODE_FULLSCREEN &&
+ baseIntent == intentToLaunch) {
+ rootCandidate = change
+ }
+ }
+
+ // If we could not find a proper root candidate, something went wrong.
+ check(rootCandidate != null) { "Could not find a split root candidate" }
+
+ // Recurse up the tree until parent is null, then we've found our root.
+ var parentToken: WindowContainerToken? = rootCandidate.parent
+ while (parentToken != null) {
+ rootCandidate = transitionInfo.getChange(parentToken) ?: break
+ parentToken = rootCandidate.parent
+ }
+
+ // Make sure nothing weird happened, like getChange() returning null.
+ check(rootCandidate != null) { "Failed to find a root leash" }
+
+ // Create a new floating view in Launcher, positioned above the launching icon
+ val drawableArea = launchingIconView.iconDrawableArea
+ val appIcon = appInfo.newIcon(launchingIconView.context)
+ appIcon.setBounds(0, 0, dp.iconSizePx, dp.iconSizePx)
+
+ val floatingView =
+ FloatingAppPairView.getFloatingAppPairView(
+ launcher,
+ drawableArea,
+ appIcon,
+ null /*appIcon2*/,
+ 0 /*dividerPos*/
+ )
+ floatingView.bringToFront()
+ launchAnimation.play(
+ getIconLaunchValueAnimator(t, dp, finishCallback, launcher, floatingView,
+ rootCandidate))
+ launchAnimation.start()
+ }
+
+ private fun getIconLaunchValueAnimator(t: Transaction,
+ dp: com.android.launcher3.DeviceProfile,
+ finishCallback: Runnable,
+ launcher: QuickstepLauncher,
+ floatingView: FloatingAppPairView,
+ rootCandidate: Change) : ValueAnimator {
+ val progressUpdater = ValueAnimator.ofFloat(0f, 1f)
+ val timings = AnimUtils.getDeviceAppPairLaunchTimings(dp.isTablet)
+ progressUpdater.setDuration(timings.getDuration().toLong())
+ progressUpdater.interpolator = Interpolators.LINEAR
+
+ // Shell animation: the apps are revealed toward end of the launch animation
+ progressUpdater.addUpdateListener { valueAnimator: ValueAnimator ->
+ val progress =
+ Interpolators.clampToProgress(
+ Interpolators.LINEAR,
+ valueAnimator.animatedFraction,
+ timings.appRevealStartOffset,
+ timings.appRevealEndOffset
+ )
+
+ // Set the alpha of the shell layer (2 apps + divider)
+ t.setAlpha(rootCandidate.leash, progress)
+ t.apply()
+ }
+
+ progressUpdater.addUpdateListener(
+ object : MultiValueUpdateListener() {
+ var mDx =
+ FloatProp(
+ floatingView.startingPosition.left,
+ dp.widthPx / 2f - floatingView.startingPosition.width() / 2f,
+ Interpolators.clampToProgress(
+ timings.getStagedRectXInterpolator(),
+ timings.stagedRectSlideStartOffset,
+ timings.stagedRectSlideEndOffset
+ )
+ )
+ var mDy =
+ FloatProp(
+ floatingView.startingPosition.top,
+ dp.heightPx / 2f - floatingView.startingPosition.height() / 2f,
+ Interpolators.clampToProgress(
+ Interpolators.EMPHASIZED,
+ timings.stagedRectSlideStartOffset,
+ timings.stagedRectSlideEndOffset
+ )
+ )
+ var mScaleX =
+ FloatProp(
+ 1f /* start */,
+ dp.widthPx / floatingView.startingPosition.width(),
+ Interpolators.clampToProgress(
+ Interpolators.EMPHASIZED,
+ timings.stagedRectSlideStartOffset,
+ timings.stagedRectSlideEndOffset
+ )
+ )
+ var mScaleY =
+ FloatProp(
+ 1f /* start */,
+ dp.heightPx / floatingView.startingPosition.height(),
+ Interpolators.clampToProgress(
+ Interpolators.EMPHASIZED,
+ timings.stagedRectSlideStartOffset,
+ timings.stagedRectSlideEndOffset
+ )
+ )
+
+ override fun onUpdate(percent: Float, initOnly: Boolean) {
+ floatingView.progress = percent
+ floatingView.x = mDx.value
+ floatingView.y = mDy.value
+ floatingView.scaleX = mScaleX.value
+ floatingView.scaleY = mScaleY.value
+ floatingView.invalidate()
+ }
+ }
+ )
+ progressUpdater.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ safeRemoveViewFromDragLayer(launcher, floatingView)
+ finishCallback.run()
+ }
+ }
+ )
+
+ return progressUpdater
+ }
+
+ /**
* This is a scale-up-and-fade-in animation (34% to 100%) for launching an app in Overview when
* there is no visible associated tile to expand from.
+ * [windowingMode] helps determine whether we are looking for a split or a single fullscreen
+ * [Change]
*/
@VisibleForTesting
fun composeScaleUpLaunchAnimation(
transitionInfo: TransitionInfo,
t: Transaction,
- finishCallback: Runnable
+ finishCallback: Runnable,
+ windowingMode: Int
) {
val launchAnimation = AnimatorSet()
val progressUpdater = ValueAnimator.ofFloat(0f, 1f)
@@ -833,9 +962,8 @@
// TODO (b/316490565): Replace this logic when SplitBounds is available to
// startAnimation() and we can know the precise taskIds of launching tasks.
- // Find a change that has WINDOWING_MODE_MULTI_WINDOW.
if (
- taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW &&
+ taskInfo.windowingMode == windowingMode &&
(change.mode == TRANSIT_OPEN || change.mode == TRANSIT_TO_FRONT)
) {
// Found one!
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/util/SystemWindowManagerProxy.java b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
index 9268511..304b8f4 100644
--- a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
+++ b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
@@ -26,9 +26,11 @@
import android.view.WindowMetrics;
import com.android.internal.policy.SystemBarUtils;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.util.window.CachedDisplayInfo;
import com.android.launcher3.util.window.WindowManagerProxy;
+import com.android.quickstep.LauncherActivityInterface;
import java.util.List;
import java.util.Set;
@@ -49,6 +51,13 @@
}
@Override
+ public boolean isInDesktopMode() {
+ DesktopVisibilityController desktopController =
+ LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
+ return desktopController != null && desktopController.areDesktopTasksVisible();
+ }
+
+ @Override
public int getRotation(Context displayInfoContext) {
return displayInfoContext.getResources().getConfiguration().windowConfiguration
.getRotation();
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
index b8afd9d..c3efc3c 100644
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -34,7 +34,6 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
import com.android.launcher3.R;
-import com.android.launcher3.statemanager.StatefulActivity;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
import com.android.quickstep.util.BorderAnimator;
@@ -136,6 +135,10 @@
* Enable or disable showing border on focus change
*/
public void setBorderEnabled(boolean enabled) {
+ if (mBorderEnabled == enabled) {
+ return;
+ }
+
mBorderEnabled = enabled;
if (mFocusBorderAnimator != null) {
mFocusBorderAnimator.setBorderVisibility(/* visible= */
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
index a0ec525..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) {
@@ -311,9 +301,13 @@
DesktopRecentsTransitionController recentsController =
recentsView.getDesktopRecentsController();
if (recentsController != null) {
- recentsController.launchDesktopFromRecents(this, success -> {
- endCallback.executeAllAndDestroy();
- });
+ recentsController.launchDesktopFromRecents(this,
+ success -> endCallback.executeAllAndDestroy());
+ Log.d(TAG, "launchTaskAnimated - launchDesktopFromRecents: " + Arrays.toString(
+ getTaskIds()));
+ } else {
+ Log.d(TAG, "launchTaskAnimated - recentsController is null: " + Arrays.toString(
+ getTaskIds()));
}
// Callbacks get run from recentsView for case when recents animation already running
diff --git a/quickstep/src/com/android/quickstep/views/FloatingAppPairBackground.kt b/quickstep/src/com/android/quickstep/views/FloatingAppPairBackground.kt
index 0d49309..e024995 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingAppPairBackground.kt
+++ b/quickstep/src/com/android/quickstep/views/FloatingAppPairBackground.kt
@@ -36,17 +36,18 @@
* animation. Consists of a rectangular background that splits into two, and two app icons that
* increase in size during the animation.
*/
-class FloatingAppPairBackground(
- context: Context,
- private val floatingView: FloatingAppPairView, // the view that we will draw this background on
- private val appIcon1: Drawable,
- private val appIcon2: Drawable,
- dividerPos: Int
+open class FloatingAppPairBackground(
+ context: Context,
+ // the view that we will draw this background on
+ protected val floatingView: FloatingAppPairView,
+ private val appIcon1: Drawable,
+ private val appIcon2: Drawable?,
+ dividerPos: Int
) : Drawable() {
companion object {
// Design specs -- app icons start small and expand during the animation
- private val STARTING_ICON_SIZE_PX = Utilities.dpToPx(22f)
- private val ENDING_ICON_SIZE_PX = Utilities.dpToPx(66f)
+ internal val STARTING_ICON_SIZE_PX = Utilities.dpToPx(22f)
+ internal val ENDING_ICON_SIZE_PX = Utilities.dpToPx(66f)
// Null values to use with drawDoubleRoundRect(), since there doesn't seem to be any other
// API for drawing rectangles with 4 different corner radii.
@@ -58,13 +59,13 @@
private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG)
// Animation interpolators
- private val expandXInterpolator: Interpolator
- private val expandYInterpolator: Interpolator
+ protected val expandXInterpolator: Interpolator
+ protected val expandYInterpolator: Interpolator
private val cellSplitInterpolator: Interpolator
- private val iconFadeInterpolator: Interpolator
+ protected val iconFadeInterpolator: Interpolator
// Device-specific measurements
- private val deviceCornerRadius: Float
+ protected val deviceCornerRadius: Float
private val deviceHalfDividerSize: Float
private val desiredSplitRatio: Float
@@ -214,7 +215,7 @@
canvas.save()
canvas.translate(changingIcon2Left, changingIconTop)
canvas.scale(changingIconScaleX, changingIconScaleY)
- appIcon2.alpha = changingIconAlpha
+ appIcon2!!.alpha = changingIconAlpha
appIcon2.draw(canvas)
canvas.restore()
}
@@ -312,7 +313,7 @@
canvas.save()
canvas.translate(changingIconLeft, changingIcon2Top)
canvas.scale(changingIconScaleX, changingIconScaleY)
- appIcon2.alpha = changingIconAlpha
+ appIcon2!!.alpha = changingIconAlpha
appIcon2.draw(canvas)
canvas.restore()
}
@@ -325,7 +326,7 @@
* @param radii An array of 8 radii for the corners: top left x, top left y, top right x, top
* right y, bottom right x, and so on.
*/
- private fun drawCustomRoundedRect(c: Canvas, rect: RectF, radii: FloatArray) {
+ protected fun drawCustomRoundedRect(c: Canvas, rect: RectF, radii: FloatArray) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// Canvas.drawDoubleRoundRect is supported from Q onward
c.drawDoubleRoundRect(rect, radii, EMPTY_RECT, ARRAY_OF_ZEROES, backgroundPaint)
diff --git a/quickstep/src/com/android/quickstep/views/FloatingAppPairView.kt b/quickstep/src/com/android/quickstep/views/FloatingAppPairView.kt
index e90aa13..e8d1cc1 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingAppPairView.kt
+++ b/quickstep/src/com/android/quickstep/views/FloatingAppPairView.kt
@@ -40,8 +40,8 @@
fun getFloatingAppPairView(
launcher: StatefulActivity<*>,
originalView: View,
- appIcon1: Drawable,
- appIcon2: Drawable,
+ appIcon1: Drawable?,
+ appIcon2: Drawable?,
dividerPos: Int
): FloatingAppPairView {
val dragLayer: ViewGroup = launcher.getDragLayer()
@@ -64,8 +64,8 @@
fun init(
launcher: StatefulActivity<*>,
originalView: View,
- appIcon1: Drawable,
- appIcon2: Drawable,
+ appIcon1: Drawable?,
+ appIcon2: Drawable?,
dividerPos: Int
) {
val viewBounds = Rect(0, 0, originalView.width, originalView.height)
@@ -92,7 +92,14 @@
layoutParams = lp
// Prepare to draw app pair icon background
- background = FloatingAppPairBackground(context, this, appIcon1, appIcon2, dividerPos)
+ background = if (appIcon1 == null || appIcon2 == null) {
+ val iconToAnimate = appIcon1 ?: appIcon2
+ checkNotNull(iconToAnimate)
+ FloatingFullscreenAppPairBackground(context, this, iconToAnimate,
+ dividerPos)
+ } else {
+ FloatingAppPairBackground(context, this, appIcon1, appIcon2, dividerPos)
+ }
background.setBounds(0, 0, lp.width, lp.height)
}
diff --git a/quickstep/src/com/android/quickstep/views/FloatingFullscreenAppPairBackground.kt b/quickstep/src/com/android/quickstep/views/FloatingFullscreenAppPairBackground.kt
new file mode 100644
index 0000000..8cd997f
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/FloatingFullscreenAppPairBackground.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.views
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.RectF
+import android.graphics.drawable.Drawable
+
+class FloatingFullscreenAppPairBackground(
+ context: Context,
+ floatingView: FloatingAppPairView,
+ private val iconToLaunch: Drawable,
+ dividerPos: Int) :
+ FloatingAppPairBackground(
+ context,
+ floatingView,
+ iconToLaunch,
+ null /*appIcon2*/,
+ dividerPos
+) {
+
+ /** Animates the background as if launching a fullscreen task. */
+ override fun draw(canvas: Canvas) {
+ val progress = floatingView.progress
+
+ // Since the entire floating app pair surface is scaling up during this animation, we
+ // scale down most of these drawn elements so that they appear the proper size on-screen.
+ val scaleFactorX = floatingView.scaleX
+ val scaleFactorY = floatingView.scaleY
+
+ // Get the bounds where we will draw the background image
+ val width = bounds.width().toFloat()
+ val height = bounds.height().toFloat()
+
+ // Get device-specific measurements
+ val cornerRadiusX = deviceCornerRadius / scaleFactorX
+ val cornerRadiusY = deviceCornerRadius / scaleFactorY
+
+ // Draw background
+ drawCustomRoundedRect(
+ canvas,
+ RectF(0f, 0f, width, height),
+ floatArrayOf(
+ cornerRadiusX,
+ cornerRadiusY,
+ cornerRadiusX,
+ cornerRadiusY,
+ cornerRadiusX,
+ cornerRadiusY,
+ cornerRadiusX,
+ cornerRadiusY,
+ )
+ )
+
+ // Calculate changing measurements for icon.
+ val changingIconSizeX =
+ (STARTING_ICON_SIZE_PX +
+ ((ENDING_ICON_SIZE_PX - STARTING_ICON_SIZE_PX) *
+ expandXInterpolator.getInterpolation(progress))) / scaleFactorX
+ val changingIconSizeY =
+ (STARTING_ICON_SIZE_PX +
+ ((ENDING_ICON_SIZE_PX - STARTING_ICON_SIZE_PX) *
+ expandYInterpolator.getInterpolation(progress))) / scaleFactorY
+
+ val changingIcon1Left = (width / 2f) - (changingIconSizeX / 2f)
+ val changingIconTop = (height / 2f) - (changingIconSizeY / 2f)
+ val changingIconScaleX = changingIconSizeX / iconToLaunch.bounds.width()
+ val changingIconScaleY = changingIconSizeY / iconToLaunch.bounds.height()
+ val changingIconAlpha =
+ (255 - (255 * iconFadeInterpolator.getInterpolation(progress))).toInt()
+
+ // Draw icon
+ canvas.save()
+ canvas.translate(changingIcon1Left, changingIconTop)
+ canvas.scale(changingIconScaleX, changingIconScaleY)
+ iconToLaunch.alpha = changingIconAlpha
+ iconToLaunch.draw(canvas)
+ canvas.restore()
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index a593712..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;
/**
@@ -60,7 +62,7 @@
*/
public class GroupedTaskView extends TaskView {
- private static final String TAG = TaskView.class.getSimpleName();
+ private static final String TAG = GroupedTaskView.class.getSimpleName();
@Nullable
private Task mSecondaryTask;
// TODO(b/336612373): Support new TTV for GroupedTaskView
@@ -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 5daafcf..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) {
@@ -1707,6 +1708,12 @@
return;
}
+ if (taskGroups == null) {
+ Log.d(TAG, "applyLoadPlan - taskGroups is null");
+ } else {
+ Log.d(TAG, "applyLoadPlan - taskGroups: " + taskGroups.stream().map(
+ GroupTask::toString).toList());
+ }
mLoadPlanEverApplied = true;
if (taskGroups == null || taskGroups.isEmpty()) {
removeTasksViewsAndClearAllButton();
@@ -1720,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
@@ -1735,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.
@@ -1819,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);
@@ -1830,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 {
@@ -1853,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);
}
@@ -1863,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));
@@ -1963,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);
@@ -2343,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);
@@ -2498,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();
@@ -2587,7 +2597,7 @@
*/
public void onGestureAnimationStart(
Task[] runningTasks, RotationTouchHelper rotationTouchHelper) {
- Log.d(TAG, "onGestureAnimationStart");
+ Log.d(TAG, "onGestureAnimationStart - runningTasks: " + Arrays.toString(runningTasks));
mActiveGestureRunningTasks = runningTasks;
// This needs to be called before the other states are set since it can create the task view
if (mOrientationState.setGestureActive(true)) {
@@ -2736,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;
}
/**
@@ -2761,6 +2768,7 @@
* is called. Also scrolls the view to this task.
*/
private void showCurrentTask(Task[] runningTasks) {
+ Log.d(TAG, "showCurrentTask - runningTasks: " + Arrays.toString(runningTasks));
if (runningTasks.length == 0) {
return;
}
@@ -4273,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);
@@ -4753,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);
@@ -4774,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();
@@ -5232,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,
@@ -5457,8 +5463,9 @@
* Called when a running recents animation has finished or canceled.
*/
public void onRecentsAnimationComplete() {
- Log.d(TAG, "onRecentsAnimationComplete - mRecentsAnimationController: "
- + mRecentsAnimationController);
+ Log.d(TAG, "onRecentsAnimationComplete "
+ + "- mRecentsAnimationController: " + mRecentsAnimationController
+ + ", mSideTaskLaunchCallback: " + mSideTaskLaunchCallback);
// At this point, the recents animation is not running and if the animation was canceled
// by a display rotation then reset this state to show the screenshot
setRunningTaskViewShowScreenshot(true);
@@ -5879,8 +5886,7 @@
}
taskView.setShowScreenshot(true);
- for (TaskIdAttributeContainer container :
- taskView.getTaskIdAttributeContainers()) {
+ for (TaskIdAttributeContainer container : taskView.getTaskIdAttributeContainers()) {
if (container == null) {
continue;
}
@@ -6012,7 +6018,8 @@
}
public void cleanupRemoteTargets() {
- Log.d(TAG, "cleanupRemoteTargets");
+ Log.d(TAG, "cleanupRemoteTargets - mRemoteTargetHandles: " + Arrays.toString(
+ mRemoteTargetHandles));
mRemoteTargetHandles = null;
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 8fd99de..1e2a259 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);
@@ -583,6 +582,10 @@
* Enable or disable showing border on hover and focus change
*/
public void setBorderEnabled(boolean enabled) {
+ if (mBorderEnabled == enabled) {
+ return;
+ }
+
mBorderEnabled = enabled;
// Set the animation correctly in case it misses the hover/focus event during state
// transition
@@ -674,10 +677,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 +699,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 +757,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 +838,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");
@@ -864,7 +850,8 @@
RunnableList callbackList = launchTasks();
Log.d("b/310064698", mTask + " - onClick - callbackList: " + callbackList);
if (callbackList != null) {
- callbackList.add(() -> Log.d("b/310064698", mTask + " - onClick - launchCompleted"));
+ callbackList.add(() -> Log.d("b/310064698", Arrays.toString(
+ getTaskIds()) + " - onClick - launchCompleted"));
}
mContainer.getStatsLogManager().logger().withItemInfo(getItemInfo())
.log(LAUNCHER_TASK_LAUNCH_TAP);
@@ -872,10 +859,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(),
@@ -897,6 +887,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
@@ -904,7 +895,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()
@@ -936,7 +927,7 @@
return null;
}
} else {
- Log.d(TAG, "launchTaskAnimated - mTask is null");
+ Log.d(TAG, "launchTaskAnimated - mTask is null" + Arrays.toString(getTaskIds()));
return null;
}
}
@@ -1006,9 +997,12 @@
callback.accept(false);
});
}
+ Log.d(TAG,
+ "launchTask - startActivityFromRecents: " + Arrays.toString(getTaskIds()));
});
} else {
callback.accept(false);
+ Log.d(TAG, "launchTask - mTask is null" + Arrays.toString(getTaskIds()));
}
}
@@ -1092,6 +1086,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) {
@@ -1100,6 +1095,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) {
@@ -1188,29 +1184,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());
}
}
@@ -1664,9 +1665,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));
@@ -1702,9 +1700,6 @@
}
for (TaskIdAttributeContainer taskContainer : mTaskIdAttributeContainer) {
- if (taskContainer == null) {
- continue;
- }
for (SystemShortcut s : getEnabledShortcuts(this,
taskContainer)) {
if (s.hasHandlerForAction(action)) {
@@ -1903,13 +1898,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
*/
@@ -1966,7 +1961,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/multivalentTests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt
index c327166..c8f7946 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt
@@ -17,8 +17,6 @@
import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_END_CONTEXTUAL_BUTTONS
import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_END_NAV_BUTTONS
import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_START_CONTEXTUAL_BUTTONS
-import com.android.systemui.shared.rotation.RotationButton
-import java.lang.IllegalStateException
import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Test
@@ -39,10 +37,9 @@
private val mockRecentsButton: ImageView = mock()
private val mockHomeButton: ImageView = mock()
private val mockImeSwitcher: ImageView = mock()
- private val mockRotationButton: RotationButton = mock()
private val mockA11yButton: ImageView = mock()
private val mockSpace: Space = mock()
- private val mockConfiguration: Configuration = mock();
+ private val mockConfiguration: Configuration = mock()
private var surfaceRotation = Surface.ROTATION_0
@@ -210,7 +207,6 @@
phoneMode = phoneMode,
surfaceRotation = surfaceRotation,
imeSwitcher = mockImeSwitcher,
- rotationButton = mockRotationButton,
a11yButton = mockA11yButton,
space = mockSpace,
)
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/quickstep/tests/src/com/android/quickstep/TaskViewTest.java b/quickstep/tests/src/com/android/quickstep/TaskViewTest.java
index 8eec903..512557b 100644
--- a/quickstep/tests/src/com/android/quickstep/TaskViewTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaskViewTest.java
@@ -17,9 +17,11 @@
package com.android.quickstep;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -34,7 +36,6 @@
import androidx.test.filters.SmallTest;
-import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.quickstep.util.BorderAnimator;
import com.android.quickstep.views.TaskView;
@@ -74,6 +75,7 @@
@Test
public void notShowBorderOnBorderDisabled() {
+ presetBorderStatus(/* enabled= */ true);
mTaskView.setBorderEnabled(/* enabled= */ false);
MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0.0f, 0.0f, 0);
mTaskView.onHoverEvent(MotionEvent.obtain(event));
@@ -86,7 +88,7 @@
}
@Test
- public void showBorderOnBorderEnabled() {
+ public void showBorderOnHoverEvent() {
mTaskView.setBorderEnabled(/* enabled= */ true);
MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0.0f, 0.0f, 0);
mTaskView.onHoverEvent(MotionEvent.obtain(event));
@@ -98,7 +100,18 @@
}
@Test
+ public void showBorderOnBorderEnabled() {
+ presetBorderStatus(/* enabled= */ false);
+ mTaskView.setBorderEnabled(/* enabled= */ true);
+ verify(mHoverAnimator, times(1)).setBorderVisibility(/* visible= */ true, /* animated= */
+ true);
+ verify(mFocusAnimator, times(1)).setBorderVisibility(/* visible= */ true, /* animated= */
+ true);
+ }
+
+ @Test
public void hideBorderOnBorderDisabled() {
+ presetBorderStatus(/* enabled= */ true);
mTaskView.setBorderEnabled(/* enabled= */ false);
verify(mHoverAnimator, times(1)).setBorderVisibility(/* visible= */ false, /* animated= */
true);
@@ -107,13 +120,35 @@
}
@Test
+ public void notTriggerAnimatorWhenEnableStatusUnchanged() {
+ presetBorderStatus(/* enabled= */ false);
+ // Border is disabled by default, no animator is triggered after it is disabled again
+ mTaskView.setBorderEnabled(/* enabled= */ false);
+ verify(mHoverAnimator, never()).setBorderVisibility(/* visible= */
+ anyBoolean(), /* animated= */ anyBoolean());
+ verify(mFocusAnimator, never()).setBorderVisibility(/* visible= */
+ anyBoolean(), /* animated= */ anyBoolean());
+ }
+
+ private void presetBorderStatus(boolean enabled) {
+ // Make the task view focused and hovered
+ MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0.0f, 0.0f, 0);
+ mTaskView.onHoverEvent(MotionEvent.obtain(event));
+ mTaskView.requestFocus();
+ mTaskView.setBorderEnabled(/* enabled= */ enabled);
+ // Reset invocation count after presetting status
+ reset(mHoverAnimator);
+ reset(mFocusAnimator);
+ }
+
+ @Test
public void notShowBorderByDefault() {
MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0.0f, 0.0f, 0);
mTaskView.onHoverEvent(MotionEvent.obtain(event));
- verify(mHoverAnimator, never()).setBorderVisibility(/* visible= */ false, /* animated= */
- true);
+ verify(mHoverAnimator, never()).setBorderVisibility(/* visible= */
+ anyBoolean(), /* animated= */ anyBoolean());
mTaskView.onFocusChanged(true, 0, new Rect());
- verify(mHoverAnimator, never()).setBorderVisibility(/* visible= */ false, /* animated= */
- true);
+ verify(mHoverAnimator, never()).setBorderVisibility(/* visible= */
+ anyBoolean(), /* animated= */ anyBoolean());
}
}
diff --git a/quickstep/tests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt b/quickstep/tests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt
index dbe4624..4d10f0f 100644
--- a/quickstep/tests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt
@@ -22,6 +22,7 @@
import androidx.test.filters.SmallTest
import com.android.launcher3.LauncherPrefs
import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING
+import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING_IN_DESKTOP_MODE
import com.android.launcher3.logging.StatsLogManager
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN
@@ -53,7 +54,13 @@
class TaskbarPinningControllerTest : TaskbarBaseTestCase() {
private val taskbarDragLayer = mock<TaskbarDragLayer>()
private val taskbarSharedState = mock<TaskbarSharedState>()
- private val launcherPrefs = mock<LauncherPrefs> { on { get(TASKBAR_PINNING) } doReturn false }
+ private var isInDesktopMode = false
+ private val isInDesktopModeProvider = { isInDesktopMode }
+ private val launcherPrefs =
+ mock<LauncherPrefs> {
+ on { get(TASKBAR_PINNING) } doReturn false
+ on { get(TASKBAR_PINNING_IN_DESKTOP_MODE) } doReturn false
+ }
private val statsLogger = mock<StatsLogManager.StatsLogger>()
private val statsLogManager = mock<StatsLogManager> { on { logger() } doReturn statsLogger }
private lateinit var pinningController: TaskbarPinningController
@@ -64,7 +71,8 @@
whenever(taskbarActivityContext.launcherPrefs).thenReturn(launcherPrefs)
whenever(taskbarActivityContext.dragLayer).thenReturn(taskbarDragLayer)
whenever(taskbarActivityContext.statsLogManager).thenReturn(statsLogManager)
- pinningController = spy(TaskbarPinningController(taskbarActivityContext))
+ pinningController =
+ spy(TaskbarPinningController(taskbarActivityContext, isInDesktopModeProvider))
pinningController.init(taskbarControllers, taskbarSharedState)
}
@@ -95,7 +103,7 @@
}
@Test
- fun testOnCloseCallback_whenPreferenceChanged_shouldAnimateToPinnedTaskbar() {
+ fun testOnCloseCallback_whenLauncherPreferenceChanged_shouldAnimateToPinnedTaskbar() {
whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(false)
doNothing().whenever(pinningController).animateTaskbarPinning(any())
@@ -106,7 +114,7 @@
}
@Test
- fun testOnCloseCallback_whenPreferenceChanged_shouldAnimateToTransientTaskbar() {
+ fun testOnCloseCallback_whenLauncherPreferenceChanged_shouldAnimateToTransientTaskbar() {
whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(true)
doNothing().whenever(pinningController).animateTaskbarPinning(any())
@@ -199,4 +207,13 @@
assertThat(pinningController.isAnimatingTaskbarPinning).isFalse()
verify(launcherPrefs, times(1)).put(TASKBAR_PINNING, true)
}
+
+ @Test
+ fun testRecreateTaskbarAndUpdatePinningValue_whenAnimationEnds_shouldUpdateTaskbarPinningDesktopModePref() {
+ isInDesktopMode = true
+ pinningController.recreateTaskbarAndUpdatePinningValue()
+ verify(taskbarDragLayer, times(1)).setAnimatingTaskbarPinning(false)
+ assertThat(pinningController.isAnimatingTaskbarPinning).isFalse()
+ verify(launcherPrefs, times(1)).put(TASKBAR_PINNING_IN_DESKTOP_MODE, true)
+ }
}
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
index 4ffb6bd..de98703 100644
--- a/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
@@ -17,6 +17,8 @@
package com.android.quickstep.util
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.view.ContextThemeWrapper
@@ -39,8 +41,10 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
import org.mockito.kotlin.any
import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
@@ -273,6 +277,9 @@
doNothing()
.whenever(spySplitAnimationController)
.composeIconSplitLaunchAnimator(any(), any(), any(), any())
+ doReturn(-1)
+ .whenever(spySplitAnimationController)
+ .hasChangesForBothAppPairs(any(), any())
spySplitAnimationController.playSplitLaunchAnimation(
null /* launchingTaskView */,
@@ -294,13 +301,45 @@
}
@Test
- fun playsAppropriateSplitLaunchAnimation_playsIconLaunchFromTaskbarContextCorrectly() {
+ fun playsAppropriateSplitLaunchAnimation_playsIconFullscreenLaunchCorrectly() {
+ val spySplitAnimationController = spy(splitAnimationController)
+ whenever(mockAppPairIcon.context).thenReturn(mockContextThemeWrapper)
+ doNothing()
+ .whenever(spySplitAnimationController)
+ .composeFullscreenIconSplitLaunchAnimator(any(), any(), any(), any(), any())
+ doReturn(0)
+ .whenever(spySplitAnimationController)
+ .hasChangesForBothAppPairs(any(), any())
+
+ spySplitAnimationController.playSplitLaunchAnimation(
+ null /* launchingTaskView */,
+ mockAppPairIcon,
+ taskId,
+ taskId2,
+ null /* apps */,
+ null /* wallpapers */,
+ null /* nonApps */,
+ stateManager,
+ depthController,
+ transitionInfo,
+ transaction,
+ {} /* finishCallback */
+ )
+
+ verify(spySplitAnimationController)
+ .composeFullscreenIconSplitLaunchAnimator(any(), any(), any(), any(), eq(0))
+ }
+
+ @Test
+ fun playsAppropriateSplitLaunchAnimation_playsIconLaunchFromTaskbarCMultiWindow() {
val spySplitAnimationController = spy(splitAnimationController)
whenever(mockAppPairIcon.context).thenReturn(mockTaskbarActivityContext)
doNothing()
.whenever(spySplitAnimationController)
- .composeScaleUpLaunchAnimation(any(), any(), any())
-
+ .composeScaleUpLaunchAnimation(any(), any(), any(), any())
+ doReturn(-1)
+ .whenever(spySplitAnimationController)
+ .hasChangesForBothAppPairs(any(), any())
spySplitAnimationController.playSplitLaunchAnimation(
null /* launchingTaskView */,
mockAppPairIcon,
@@ -316,7 +355,37 @@
{} /* finishCallback */
)
- verify(spySplitAnimationController).composeScaleUpLaunchAnimation(any(), any(), any())
+ verify(spySplitAnimationController).composeScaleUpLaunchAnimation(any(), any(), any(),
+ eq(WINDOWING_MODE_MULTI_WINDOW))
+ }
+
+ @Test
+ fun playsAppropriateSplitLaunchAnimation_playsIconLaunchFromTaskbarFullscreen() {
+ val spySplitAnimationController = spy(splitAnimationController)
+ whenever(mockAppPairIcon.context).thenReturn(mockTaskbarActivityContext)
+ doNothing()
+ .whenever(spySplitAnimationController)
+ .composeScaleUpLaunchAnimation(any(), any(), any(), any())
+ doReturn(0)
+ .whenever(spySplitAnimationController)
+ .hasChangesForBothAppPairs(any(), any())
+ spySplitAnimationController.playSplitLaunchAnimation(
+ null /* launchingTaskView */,
+ mockAppPairIcon,
+ taskId,
+ taskId2,
+ null /* apps */,
+ null /* wallpapers */,
+ null /* nonApps */,
+ stateManager,
+ depthController,
+ transitionInfo,
+ transaction,
+ {} /* finishCallback */
+ )
+
+ verify(spySplitAnimationController).composeScaleUpLaunchAnimation(any(), any(), any(),
+ eq(WINDOWING_MODE_FULLSCREEN))
}
@Test
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 98dade5..7e9e864 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -33,7 +33,6 @@
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
-import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
@@ -115,7 +114,6 @@
@Thunk final int[] mTempLocation = new int[2];
@Thunk final Rect mTempOnDrawCellToRect = new Rect();
- final PointF mTmpPointF = new PointF();
protected GridOccupancy mOccupied;
public GridOccupancy mTmpOccupied;
@@ -197,16 +195,11 @@
public static final int REORDER_ANIMATION_DURATION = 150;
@Thunk final float mReorderPreviewAnimationMagnitude;
- private final ArrayList<View> mIntersectingViews = new ArrayList<>();
- private final Rect mOccupiedRect = new Rect();
public final int[] mDirectionVector = new int[2];
ItemConfiguration mPreviousSolution = null;
- private static final int INVALID_DIRECTION = -100;
private final Rect mTempRect = new Rect();
- private final RectF mTempRectF = new RectF();
- private final float[] mTmpFloatArray = new float[4];
private static final Paint sPaint = new Paint();
@@ -1163,9 +1156,6 @@
mDragCellSpan[0] = spanX;
mDragCellSpan[1] = spanY;
- // Apply color extraction on a widget when dragging.
- applyColorExtractionOnWidget(dragObject, mDragCell, spanX, spanY);
-
final int oldIndex = mDragOutlineCurrent;
mDragOutlineAnims[oldIndex].animateOut();
mDragOutlineCurrent = (oldIndex + 1) % mDragOutlines.length;
@@ -1186,19 +1176,6 @@
}
}
- /** Applies the local color extraction to a dragging widget object. */
- private void applyColorExtractionOnWidget(DropTarget.DragObject dragObject, int[] targetCell,
- int spanX, int spanY) {
- // Apply local extracted color if the DragView is an AppWidgetHostViewDrawable.
- View view = dragObject.dragView.getContentView();
- if (view instanceof LauncherAppWidgetHostView) {
- int screenId = mCellLayoutContainer.getCellLayoutId(this);
- cellToRect(targetCell[0], targetCell[1], spanX, spanY, mTempRect);
-
- ((LauncherAppWidgetHostView) view).handleDrag(mTempRect, this, screenId);
- }
- }
-
@SuppressLint("StringFormatMatches")
public String getItemMoveDescription(int cellX, int cellY) {
if (mContainerType == HOTSEAT) {
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index f405b93..54aea38 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.Utilities.dpiFromPx;
import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE;
import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
+import static com.android.launcher3.util.DisplayController.CHANGE_DESKTOP_MODE;
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
import static com.android.launcher3.util.DisplayController.CHANGE_SUPPORTED_BOUNDS;
import static com.android.launcher3.util.DisplayController.CHANGE_TASKBAR_PINNING;
@@ -236,7 +237,8 @@
DisplayController.INSTANCE.get(context).setPriorityListener(
(displayContext, info, flags) -> {
if ((flags & (CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS
- | CHANGE_NAVIGATION_MODE | CHANGE_TASKBAR_PINNING)) != 0) {
+ | CHANGE_NAVIGATION_MODE | CHANGE_TASKBAR_PINNING
+ | CHANGE_DESKTOP_MODE)) != 0) {
onConfigChanged(displayContext);
}
});
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/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index 6a5001b..13181e8 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -253,6 +253,7 @@
@JvmStatic fun get(context: Context): LauncherPrefs = INSTANCE.get(context)
const val TASKBAR_PINNING_KEY = "TASKBAR_PINNING_KEY"
+ const val TASKBAR_PINNING_DESKTOP_MODE_KEY = "TASKBAR_PINNING_DESKTOP_MODE_KEY"
const val SHOULD_SHOW_SMARTSPACE_KEY = "SHOULD_SHOW_SMARTSPACE_KEY"
@JvmField
val ICON_STATE = nonRestorableItem("pref_icon_shape_path", "", EncryptionType.ENCRYPTED)
@@ -272,6 +273,9 @@
@JvmField
val TASKBAR_PINNING =
backedUpItem(TASKBAR_PINNING_KEY, false, EncryptionType.DEVICE_PROTECTED)
+ @JvmField
+ val TASKBAR_PINNING_IN_DESKTOP_MODE =
+ backedUpItem(TASKBAR_PINNING_DESKTOP_MODE_KEY, true, EncryptionType.DEVICE_PROTECTED)
@JvmField
val DEVICE_TYPE =
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index beab8c1..584d089 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -126,7 +126,6 @@
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.PendingAppWidgetHostView;
import com.android.launcher3.widget.WidgetManagerHelper;
-import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
import com.android.launcher3.widget.util.WidgetSizes;
import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks;
import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayTouchProxy;
@@ -1744,9 +1743,6 @@
final DragView dv;
if (contentView instanceof View) {
- if (contentView instanceof LauncherAppWidgetHostView) {
- mDragController.addDragListener(new AppWidgetHostViewDragListener(mLauncher));
- }
dv = mDragController.startDrag(
contentView,
draggableView,
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);
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 441bbb5..2f3c2b6 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -1205,7 +1205,7 @@
* Creates a new instance of {@link StatsLogManager} based on provided context.
*/
public static StatsLogManager newInstance(Context context) {
- return Overrides.getObject(StatsLogManager.class,
- context.getApplicationContext(), R.string.stats_log_manager_class);
+ return Overrides.getObject(
+ StatsLogManager.class, context, R.string.stats_log_manager_class);
}
}
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 51bc339..eea1a7d 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -51,7 +51,7 @@
public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
public static final String TAG = "StateManager";
- // b/279059025
+ // b/279059025, b/325463989
private static final boolean DEBUG = true;
private final AnimationState mConfig = new AnimationState();
@@ -240,16 +240,8 @@
private void goToState(
STATE_TYPE state, boolean animated, long delay, AnimatorListener listener) {
if (DEBUG) {
- String stackTrace = Log.getStackTraceString(new Exception("tracing state transition"));
- String truncatedTrace =
- Arrays.stream(stackTrace.split("\\n"))
- .limit(5)
- .skip(1) // Removes the line "java.lang.Exception: tracing state
- // transition"
- .filter(traceLine -> !traceLine.contains("StateManager.goToState"))
- .collect(Collectors.joining("\n"));
Log.d(TAG, "goToState - fromState: " + mState + ", toState: " + state
- + ", partial trace:\n" + truncatedTrace);
+ + ", partial trace:\n" + getTrimmedStackTrace("StateManager.goToState"));
}
animated &= areAnimatorsEnabled();
@@ -336,17 +328,9 @@
public AnimatorSet createAtomicAnimation(
STATE_TYPE fromState, STATE_TYPE toState, StateAnimationConfig config) {
if (DEBUG) {
- String stackTrace = Log.getStackTraceString(new Exception("tracing state transition"));
- String truncatedTrace =
- Arrays.stream(stackTrace.split("\\n"))
- .limit(5)
- .skip(1) // Removes the line "java.lang.Exception: tracing state
- // transition"
- .filter(traceLine -> !traceLine.contains(
- "StateManager.createAtomicAnimation"))
- .collect(Collectors.joining("\n"));
Log.d(TAG, "createAtomicAnimation - fromState: " + fromState + ", toState: " + toState
- + ", partial trace:\n" + truncatedTrace);
+ + ", partial trace:\n" + getTrimmedStackTrace(
+ "StateManager.createAtomicAnimation"));
}
PendingAnimation builder = new PendingAnimation(config.duration);
@@ -481,7 +465,8 @@
*/
public void cancelAnimation() {
if (DEBUG && mConfig.currentAnimation != null) {
- Log.d(TAG, "cancelAnimation - with ongoing animation");
+ Log.d(TAG, "cancelAnimation - with ongoing animation"
+ + ", partial trace:\n" + getTrimmedStackTrace("StateManager.cancelAnimation"));
}
mConfig.reset();
// It could happen that a new animation is set as a result of an endListener on the
@@ -579,6 +564,15 @@
mConfig.playbackController = null;
}
+ private String getTrimmedStackTrace(String callingMethodName) {
+ String stackTrace = Log.getStackTraceString(new Exception());
+ return Arrays.stream(stackTrace.split("\\n"))
+ .skip(2) // Removes the line "java.lang.Exception" and "getTrimmedStackTrace".
+ .filter(traceLine -> !traceLine.contains(callingMethodName))
+ .limit(3)
+ .collect(Collectors.joining("\n"));
+ }
+
private class StartAnimRunnable implements Runnable {
private final AnimatorSet mAnim;
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 8806e27..92fc38f 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -23,6 +23,8 @@
import static com.android.launcher3.InvariantDeviceProfile.TYPE_PHONE;
import static com.android.launcher3.InvariantDeviceProfile.TYPE_TABLET;
import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING;
+import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING_DESKTOP_MODE_KEY;
+import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING_IN_DESKTOP_MODE;
import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING_KEY;
import static com.android.launcher3.Utilities.dpiFromPx;
import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning;
@@ -88,10 +90,11 @@
public static final int CHANGE_SUPPORTED_BOUNDS = 1 << 3;
public static final int CHANGE_NAVIGATION_MODE = 1 << 4;
public static final int CHANGE_TASKBAR_PINNING = 1 << 5;
+ public static final int CHANGE_DESKTOP_MODE = 1 << 6;
public static final int CHANGE_ALL = CHANGE_ACTIVE_SCREEN | CHANGE_ROTATION
| CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS | CHANGE_NAVIGATION_MODE
- | CHANGE_TASKBAR_PINNING;
+ | CHANGE_TASKBAR_PINNING | CHANGE_DESKTOP_MODE;
private static final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
private static final String TARGET_OVERLAY_PACKAGE = "android";
@@ -145,16 +148,22 @@
private void attachTaskbarPinningSharedPreferenceChangeListener(Context context) {
mTaskbarPinningPreferenceChangeListener =
(sharedPreferences, key) -> {
- if (TASKBAR_PINNING_KEY.equals(key)
- && mInfo.mIsTaskbarPinned != LauncherPrefs.get(mContext).get(
- TASKBAR_PINNING)
- ) {
+ LauncherPrefs prefs = LauncherPrefs.get(mContext);
+ boolean isTaskbarPinningChanged = TASKBAR_PINNING_KEY.equals(key)
+ && mInfo.mIsTaskbarPinned != prefs.get(TASKBAR_PINNING);
+ boolean isTaskbarPinningDesktopModeChanged =
+ TASKBAR_PINNING_DESKTOP_MODE_KEY.equals(key)
+ && mInfo.mIsTaskbarPinnedInDesktopMode != prefs.get(
+ TASKBAR_PINNING_IN_DESKTOP_MODE);
+ if (isTaskbarPinningChanged || isTaskbarPinningDesktopModeChanged) {
handleInfoChange(mWindowContext.getDisplay());
}
};
LauncherPrefs.get(context).addListener(
mTaskbarPinningPreferenceChangeListener, TASKBAR_PINNING);
+ LauncherPrefs.get(context).addListener(
+ mTaskbarPinningPreferenceChangeListener, TASKBAR_PINNING_IN_DESKTOP_MODE);
}
/**
@@ -172,6 +181,13 @@
}
/**
+ * Handles info change for desktop mode.
+ */
+ public static void handleInfoChangeForDesktopMode(Context context) {
+ INSTANCE.get(context).handleInfoChange(context.getDisplay());
+ }
+
+ /**
* Enables transient taskbar status for tests.
*/
@VisibleForTesting
@@ -192,6 +208,8 @@
if (enableTaskbarPinning()) {
LauncherPrefs.get(mContext).removeListener(
mTaskbarPinningPreferenceChangeListener, TASKBAR_PINNING);
+ LauncherPrefs.get(mContext).removeListener(
+ mTaskbarPinningPreferenceChangeListener, TASKBAR_PINNING_IN_DESKTOP_MODE);
}
if (mWindowContext != null) {
mWindowContext.unregisterComponentCallbacks(this);
@@ -309,9 +327,15 @@
FileLog.w(TAG,
"(CHANGE_SUPPORTED_BOUNDS) perDisplayBounds: " + newInfo.mPerDisplayBounds);
}
- if (newInfo.mIsTaskbarPinned != oldInfo.mIsTaskbarPinned) {
+ if ((newInfo.mIsTaskbarPinned != oldInfo.mIsTaskbarPinned)
+ || (newInfo.mIsTaskbarPinnedInDesktopMode
+ != oldInfo.mIsTaskbarPinnedInDesktopMode)) {
change |= CHANGE_TASKBAR_PINNING;
}
+ if (newInfo.mIsInDesktopMode != oldInfo.mIsInDesktopMode) {
+ change |= CHANGE_DESKTOP_MODE;
+ }
+
if (DEBUG) {
Log.d(TAG, "handleInfoChange - change: " + getChangeFlagsString(change));
}
@@ -355,6 +379,9 @@
new ArrayMap<>();
private final boolean mIsTaskbarPinned;
+ private final boolean mIsTaskbarPinnedInDesktopMode;
+
+ private final boolean mIsInDesktopMode;
public Info(Context displayInfoContext) {
/* don't need system overrides for external displays */
@@ -414,6 +441,9 @@
}
mIsTaskbarPinned = LauncherPrefs.get(displayInfoContext).get(TASKBAR_PINNING);
+ mIsTaskbarPinnedInDesktopMode = LauncherPrefs.get(displayInfoContext).get(
+ TASKBAR_PINNING_IN_DESKTOP_MODE);
+ mIsInDesktopMode = wmProxy.isInDesktopMode();
}
/**
@@ -430,10 +460,14 @@
return sTransientTaskbarStatusForTests;
}
if (enableTaskbarPinning()) {
+ if (mIsInDesktopMode) {
+ return !mIsTaskbarPinnedInDesktopMode;
+ }
return !mIsTaskbarPinned;
}
return true;
}
+
/**
* Returns whether the taskbar is pinned in gesture navigation mode.
*/
@@ -441,6 +475,10 @@
return navigationMode == NavigationMode.NO_BUTTON && !isTransientTaskbar();
}
+ public boolean isInDesktopMode() {
+ return mIsInDesktopMode;
+ }
+
/**
* Returns {@code true} if the bounds represent a tablet.
*/
@@ -501,6 +539,7 @@
appendFlag(result, change, CHANGE_SUPPORTED_BOUNDS, "CHANGE_SUPPORTED_BOUNDS");
appendFlag(result, change, CHANGE_NAVIGATION_MODE, "CHANGE_NAVIGATION_MODE");
appendFlag(result, change, CHANGE_TASKBAR_PINNING, "CHANGE_TASKBAR_VARIANT");
+ appendFlag(result, change, CHANGE_DESKTOP_MODE, "CHANGE_DESKTOP_MODE");
return result.toString();
}
@@ -516,6 +555,8 @@
pw.println(" densityDpi=" + info.densityDpi);
pw.println(" navigationMode=" + info.navigationMode.name());
pw.println(" isTaskbarPinned=" + info.mIsTaskbarPinned);
+ pw.println(" isTaskbarPinnedInDesktopMode=" + info.mIsTaskbarPinnedInDesktopMode);
+ pw.println(" isInDesktopMode=" + info.mIsInDesktopMode);
pw.println(" currentSize=" + info.currentSize);
info.mPerDisplayBounds.forEach((key, value) -> pw.println(
" perDisplayBounds - " + key + ": " + value));
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index 4b004f3..0817c0a 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -115,6 +115,13 @@
}
/**
+ * Returns if we are in desktop mode or not.
+ */
+ public boolean isInDesktopMode() {
+ return false;
+ }
+
+ /**
* Returns the real bounds for the provided display after applying any insets normalization
*/
public WindowBounds getRealBounds(Context displayInfoContext, CachedDisplayInfo info) {
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index c3e9ad6..44ab966 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -42,7 +42,6 @@
import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
@@ -52,8 +51,7 @@
* {@inheritDoc}
*/
public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView
- implements TouchCompleteListener, View.OnLongClickListener,
- LocalColorExtractor.Listener {
+ implements TouchCompleteListener, View.OnLongClickListener {
private static final String TAG = "LauncherAppWidgetHostView";
@@ -70,13 +68,9 @@
private static final String TRACE_METHOD_NAME = "appwidget load-widget ";
- private final Rect mTempRect = new Rect();
private final CheckLongPressHelper mLongPressHelper;
protected final ActivityContext mActivityContext;
- // Maintain the color manager.
- private final LocalColorExtractor mColorExtractor;
-
private boolean mIsScrollable;
private boolean mIsAttachedToWindow;
private boolean mIsAutoAdvanceRegistered;
@@ -84,11 +78,6 @@
private long mDeferUpdatesUntilMillis = 0;
RemoteViews mLastRemoteViews;
- private boolean mHasDeferredColorChange = false;
- private @Nullable SparseIntArray mDeferredColorChange = null;
-
- // The following member variables are only used during drag-n-drop.
- private boolean mIsInDragMode = false;
private boolean mTrackingWidgetUpdate = false;
@@ -109,7 +98,6 @@
if (Themes.getAttrBoolean(context, R.attr.isWorkspaceDarkText)) {
setOnLightBackground(true);
}
- mColorExtractor = new LocalColorExtractor(); // no-op
}
@Override
@@ -177,8 +165,8 @@
}
/**
- * Returns true if the application of {@link RemoteViews} through {@link #updateAppWidget} and
- * colors through {@link #onColorsChanged} are currently being deferred.
+ * Returns true if the application of {@link RemoteViews} through {@link #updateAppWidget} are
+ * currently being deferred.
* @see #beginDeferringUpdates()
*/
private boolean isDeferringUpdates() {
@@ -187,9 +175,8 @@
/**
* Begin deferring the application of any {@link RemoteViews} updates made through
- * {@link #updateAppWidget} and color changes through {@link #onColorsChanged} until
- * {@link #endDeferringUpdates()} has been called or the next {@link #updateAppWidget} or
- * {@link #onColorsChanged} call after {@link #UPDATE_LOCK_TIMEOUT_MILLIS} have elapsed.
+ * {@link #updateAppWidget} until {@link #endDeferringUpdates()} has been called or the next
+ * {@link #updateAppWidget} call after {@link #UPDATE_LOCK_TIMEOUT_MILLIS} have elapsed.
*/
public void beginDeferringUpdates() {
mDeferUpdatesUntilMillis = SystemClock.uptimeMillis() + UPDATE_LOCK_TIMEOUT_MILLIS;
@@ -197,26 +184,16 @@
/**
* Stop deferring the application of {@link RemoteViews} updates made through
- * {@link #updateAppWidget} and color changes made through {@link #onColorsChanged} and apply
- * any deferred updates.
+ * {@link #updateAppWidget} and apply any deferred updates.
*/
public void endDeferringUpdates() {
RemoteViews remoteViews;
- SparseIntArray deferredColors;
- boolean hasDeferredColors;
mDeferUpdatesUntilMillis = 0;
remoteViews = mLastRemoteViews;
- deferredColors = mDeferredColorChange;
- hasDeferredColors = mHasDeferredColorChange;
- mDeferredColorChange = null;
- mHasDeferredColorChange = false;
if (remoteViews != null) {
updateAppWidget(remoteViews);
}
- if (hasDeferredColors) {
- onColorsChanged(deferredColors);
- }
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
@@ -242,7 +219,6 @@
super.onAttachedToWindow();
mIsAttachedToWindow = true;
checkIfAutoAdvance();
- mColorExtractor.setListener(this);
}
@Override
@@ -253,7 +229,6 @@
// state is updated. So isAttachedToWindow() will return true until next frame.
mIsAttachedToWindow = false;
checkIfAutoAdvance();
- mColorExtractor.setListener(null);
}
@Override
@@ -292,29 +267,6 @@
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mIsScrollable = checkScrollableRecursively(this);
-
- if (!mIsInDragMode && getTag() instanceof LauncherAppWidgetInfo info) {
- mTempRect.set(left, top, right, bottom);
- mColorExtractor.setWorkspaceLocation(mTempRect, (View) getParent(), info.screenId);
- }
- }
-
- /** Starts the drag mode. */
- public void startDrag() {
- mIsInDragMode = true;
- }
-
- /** Handles a drag event occurred on a workspace page corresponding to the {@code screenId}. */
- public void handleDrag(Rect rectInView, View view, int screenId) {
- if (mIsInDragMode) {
- mColorExtractor.setWorkspaceLocation(rectInView, view, screenId);
- }
- }
-
- /** Ends the drag mode. */
- public void endDrag() {
- mIsInDragMode = false;
- requestLayout();
}
/**
@@ -338,20 +290,6 @@
}
@Override
- public void onColorsChanged(SparseIntArray colors) {
- if (isDeferringUpdates()) {
- mDeferredColorChange = colors;
- mHasDeferredColorChange = true;
- return;
- }
- mDeferredColorChange = null;
- mHasDeferredColorChange = false;
-
- // setColorResources will reapply the view, which must happen in the UI thread.
- post(() -> setColorResources(colors));
- }
-
- @Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(getClass().getName());
diff --git a/src/com/android/launcher3/widget/LocalColorExtractor.java b/src/com/android/launcher3/widget/LocalColorExtractor.java
index 96e7531..7b500c7 100644
--- a/src/com/android/launcher3/widget/LocalColorExtractor.java
+++ b/src/com/android/launcher3/widget/LocalColorExtractor.java
@@ -17,11 +17,8 @@
package com.android.launcher3.widget;
import android.app.WallpaperColors;
-import android.appwidget.AppWidgetHostView;
import android.content.Context;
-import android.graphics.Rect;
import android.util.SparseIntArray;
-import android.view.View;
import androidx.annotation.Nullable;
@@ -31,18 +28,6 @@
/** Extracts the colors we need from the wallpaper at given locations. */
public class LocalColorExtractor implements ResourceBasedOverride {
- /** Listener for color changes on a screen location. */
- public interface Listener {
- /**
- * Method called when the colors on a registered location has changed.
- *
- * {@code extractedColors} maps the color resources {@code android.R.colors.system_*} to
- * their value, in a format that can be passed directly to
- * {@link AppWidgetHostView#setColorResources(SparseIntArray)}.
- */
- void onColorsChanged(SparseIntArray extractedColors);
- }
-
/**
* Creates a new instance of LocalColorExtractor
*/
@@ -51,19 +36,6 @@
R.string.local_colors_extraction_class);
}
- /** Sets the object that will receive the color changes. */
- public void setListener(@Nullable Listener listener) {
- // no-op
- }
-
- /**
- * Sets the location used for color extraction
- * @param pos position to use for color extraction
- * @param child view whose coordinate space is used for {@code pos}
- * @param screenId the workspace screenId
- */
- public void setWorkspaceLocation(Rect pos, View child, int screenId) { }
-
/**
* Updates the base context to contain the colors override
*/
@@ -76,5 +48,4 @@
public SparseIntArray generateColorsOverride(WallpaperColors colors) {
return null;
}
-
}
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index faad307..8857774 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -44,7 +44,6 @@
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.RoundDrawableWrapper;
-import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
/**
* Extension of {@link DragPreviewProvider} with logic specific to pending widgets/shortcuts
@@ -132,8 +131,6 @@
}
if (mAppWidgetHostViewPreview != null) {
previewSizeBeforeScale[0] = mAppWidgetHostViewPreview.getMeasuredWidth();
- launcher.getDragController()
- .addDragListener(new AppWidgetHostViewDragListener(launcher));
}
if (preview == null && mAppWidgetHostViewPreview == null) {
Drawable p = new FastBitmapDrawable(new DatabaseWidgetPreviewLoader(launcher)
diff --git a/src/com/android/launcher3/widget/dragndrop/AppWidgetHostViewDragListener.java b/src/com/android/launcher3/widget/dragndrop/AppWidgetHostViewDragListener.java
deleted file mode 100644
index 3e54b33..0000000
--- a/src/com/android/launcher3/widget/dragndrop/AppWidgetHostViewDragListener.java
+++ /dev/null
@@ -1,48 +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.widget.dragndrop;
-
-import com.android.launcher3.DropTarget;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.dragndrop.DragController;
-import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.widget.LauncherAppWidgetHostView;
-
-/** A drag listener of {@link LauncherAppWidgetHostView}. */
-public final class AppWidgetHostViewDragListener implements DragController.DragListener {
- private final Launcher mLauncher;
- private LauncherAppWidgetHostView mAppWidgetHostView;
-
- public AppWidgetHostViewDragListener(Launcher launcher) {
- mLauncher = launcher;
- }
-
- @Override
- public void onDragStart(DropTarget.DragObject dragObject, DragOptions unused) {
- if (dragObject.dragView.getContentView() instanceof LauncherAppWidgetHostView) {
- mAppWidgetHostView = (LauncherAppWidgetHostView) dragObject.dragView.getContentView();
- mAppWidgetHostView.startDrag();
- } else {
- mLauncher.getDragController().removeDragListener(this);
- }
- }
-
- @Override
- public void onDragEnd() {
- mAppWidgetHostView.endDrag();
- mLauncher.getDragController().removeDragListener(this);
- }
-}
diff --git a/tests/Launcher3Tests.xml b/tests/Launcher3Tests.xml
index 29c34be..270a610 100644
--- a/tests/Launcher3Tests.xml
+++ b/tests/Launcher3Tests.xml
@@ -44,6 +44,8 @@
<option name="run-command" value="settings put global airplane_mode_on 1" />
<option name="run-command" value="am broadcast -a android.intent.action.AIRPLANE_MODE" />
+ <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/>
+
<option name="run-command" value="settings put system pointer_location 1" />
<option name="run-command" value="settings put system show_touches 1" />
</target_preparer>
diff --git a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index aefc2db..e378733 100644
--- a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -306,6 +306,7 @@
context.putObject(LauncherPrefs.INSTANCE, launcherPrefs)
whenever(launcherPrefs.get(LauncherPrefs.TASKBAR_PINNING)).thenReturn(false)
+ whenever(launcherPrefs.get(LauncherPrefs.TASKBAR_PINNING_IN_DESKTOP_MODE)).thenReturn(true)
val info = spy(DisplayController.Info(context, windowManagerProxy, perDisplayBoundsCache))
whenever(displayController.info).thenReturn(info)
whenever(info.isTransientTaskbar).thenReturn(isGestureMode)
diff --git a/tests/src/com/android/launcher3/util/DisplayControllerTest.kt b/tests/src/com/android/launcher3/util/DisplayControllerTest.kt
index 2e57ad5..273f0c4 100644
--- a/tests/src/com/android/launcher3/util/DisplayControllerTest.kt
+++ b/tests/src/com/android/launcher3/util/DisplayControllerTest.kt
@@ -31,6 +31,7 @@
import androidx.test.filters.SmallTest
import com.android.launcher3.LauncherPrefs
import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING
+import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING_IN_DESKTOP_MODE
import com.android.launcher3.util.DisplayController.CHANGE_DENSITY
import com.android.launcher3.util.DisplayController.CHANGE_ROTATION
import com.android.launcher3.util.DisplayController.CHANGE_TASKBAR_PINNING
@@ -94,6 +95,7 @@
whenever(context.getObject(eq(WindowManagerProxy.INSTANCE))).thenReturn(windowManagerProxy)
whenever(context.getObject(eq(LauncherPrefs.INSTANCE))).thenReturn(launcherPrefs)
whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(false)
+ whenever(launcherPrefs.get(TASKBAR_PINNING_IN_DESKTOP_MODE)).thenReturn(true)
// Mock WindowManagerProxy
val displayInfo = CachedDisplayInfo(Point(width, height), Surface.ROTATION_0)
@@ -170,4 +172,13 @@
verify(displayInfoChangeListener)
.onDisplayInfoChanged(any(), any(), eq(CHANGE_TASKBAR_PINNING))
}
+
+ @Test
+ @UiThreadTest
+ fun testTaskbarPinningChangeInDesktopMode() {
+ whenever(launcherPrefs.get(TASKBAR_PINNING_IN_DESKTOP_MODE)).thenReturn(false)
+ displayController.handleInfoChange(display)
+ verify(displayInfoChangeListener)
+ .onDisplayInfoChanged(any(), any(), eq(CHANGE_TASKBAR_PINNING))
+ }
}