diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml index eb9c5f0029..71855eb61f 100644 --- a/quickstep/res/values/strings.xml +++ b/quickstep/res/values/strings.xml @@ -26,6 +26,8 @@ Pin Freeform + + Desktop No recent items diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt index 10733fbbea..64fe30c538 100644 --- a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt +++ b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt @@ -56,6 +56,11 @@ class DesktopRecentsTransitionController( systemUiProxy.showDesktopApps(desktopTaskView.display.displayId, transition) } + /** Launch desktop tasks from recents view */ + fun moveToDesktop(taskId: Int) { + systemUiProxy.moveToDesktop(taskId) + } + private class RemoteDesktopLaunchTransitionRunner( private val desktopTaskView: DesktopTaskView, private val stateManager: StateManager<*>, @@ -99,8 +104,7 @@ class DesktopRecentsTransitionController( finishCallback: IRemoteTransitionFinishedCallback ) {} - override fun onTransitionConsumed(transition: IBinder?, aborted: Boolean) { - } + override fun onTransitionConsumed(transition: IBinder?, aborted: Boolean) {} } companion object { diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java index 672bd1decf..1c5a75dd1c 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java @@ -499,7 +499,7 @@ public class HotseatPredictionController implements DragController.DragListener, @Override public void onClick(View view) { - dismissTaskMenuView(mTarget); + dismissTaskMenuView(); pinPrediction(mItemInfo); } } diff --git a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt new file mode 100644 index 0000000000..8c71d925e4 --- /dev/null +++ b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.quickstep + +import android.view.View +import com.android.launcher3.AbstractFloatingViewHelper +import com.android.launcher3.BaseDraggingActivity +import com.android.launcher3.R +import com.android.launcher3.logging.StatsLogManager.LauncherEvent +import com.android.launcher3.popup.SystemShortcut +import com.android.quickstep.views.RecentsView +import com.android.quickstep.views.TaskView.TaskIdAttributeContainer +import com.android.window.flags.Flags + +/** A menu item, "Desktop", that allows the user to bring the current app into Desktop Windowing. */ +class DesktopSystemShortcut( + activity: BaseDraggingActivity, + private val mTaskContainer: TaskIdAttributeContainer, + abstractFloatingViewHelper: AbstractFloatingViewHelper +) : + SystemShortcut( + R.drawable.ic_caption_desktop_button_foreground, + R.string.recent_task_option_desktop, + activity, + mTaskContainer.itemInfo, + mTaskContainer.taskView, + abstractFloatingViewHelper + ) { + override fun onClick(view: View) { + dismissTaskMenuView() + val recentsView = mTarget!!.getOverviewPanel>() + recentsView.moveTaskToDesktop(mTaskContainer) { + mTarget.statsLogManager + .logger() + .withItemInfo(mTaskContainer.itemInfo) + .log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_DESKTOP_TAP) + } + } + + companion object { + /** Creates a factory for creating Desktop system shorcuts. */ + @JvmOverloads + fun createFactory( + abstractFloatingViewHelper: AbstractFloatingViewHelper = AbstractFloatingViewHelper() + ): TaskShortcutFactory { + return object : TaskShortcutFactory { + override fun getShortcuts( + activity: BaseDraggingActivity, + taskContainer: TaskIdAttributeContainer + ): List? { + return if (!Flags.enableDesktopWindowingMode()) null + else if (!taskContainer.task.isDockable) null + else + listOf( + DesktopSystemShortcut( + activity, + taskContainer, + abstractFloatingViewHelper + ) + ) + } + + override fun showForSplitscreen() = true + } + } + } +} diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index ab609fd686..53c1d50ee9 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -1473,6 +1473,17 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle { } } + /** Call shell to move a task with given `taskId` to desktop */ + public void moveToDesktop(int taskId) { + if (mDesktopMode != null) { + try { + mDesktopMode.moveToDesktop(taskId); + } catch (RemoteException e) { + Log.w(TAG, "Failed call moveToDesktop", e); + } + } + } + // // Unfold transition // diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java index cc582d1e09..8a4989bc98 100644 --- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java +++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java @@ -136,6 +136,7 @@ public class TaskOverlayFactory implements ResourceBasedOverride { TaskShortcutFactory.PIN, TaskShortcutFactory.INSTALL, TaskShortcutFactory.FREE_FORM, + DesktopSystemShortcut.Companion.createFactory(), TaskShortcutFactory.WELLBEING, TaskShortcutFactory.SAVE_APP_PAIR }; @@ -315,7 +316,7 @@ public class TaskOverlayFactory implements ResourceBasedOverride { @Override public void onClick(View view) { saveScreenshot(mThumbnailView.getTaskView().getTask()); - dismissTaskMenuView(mActivity); + dismissTaskMenuView(); } } diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java index 147a3e2408..5e970f1bd7 100644 --- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java +++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java @@ -145,7 +145,7 @@ public interface TaskShortcutFactory { @Override public void onClick(View view) { - dismissTaskMenuView(mTarget); + dismissTaskMenuView(); ((RecentsView) mTarget.getOverviewPanel()) .getSplitSelectController().getAppPairsController().saveAppPair(mTaskView); } @@ -174,7 +174,7 @@ public interface TaskShortcutFactory { @Override public void onClick(View view) { - dismissTaskMenuView(mTarget); + dismissTaskMenuView(); RecentsView rv = mTarget.getOverviewPanel(); rv.switchToScreenshot(() -> { rv.finishRecentsAnimation(true /* toRecents */, false /* shouldPip */, () -> { @@ -420,7 +420,7 @@ public interface TaskShortcutFactory { SystemUiProxy.INSTANCE.get(mTarget).startScreenPinning( mTaskView.getTask().key.id); } - dismissTaskMenuView(mTarget); + dismissTaskMenuView(); mTarget.getStatsLogManager().logger().withItemInfo(mTaskView.getItemInfo()) .log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_PIN_TAP); } diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index eca70b74ba..7c54cecf32 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -6200,6 +6200,27 @@ public abstract class RecentsView mActivity.setLocusContext(id, Bundle.EMPTY)); } + /** + * Moves the provided task into desktop mode, and invoke {@code successCallback} if succeeded. + */ + public void moveTaskToDesktop(TaskIdAttributeContainer taskContainer, + Runnable successCallback) { + if (!enableDesktopWindowingMode()) { + return; + } + switchToScreenshot(() -> finishRecentsAnimation(/* toRecents= */true, /* shouldPip= */false, + () -> moveTaskToDesktopInternal(taskContainer, successCallback))); + } + + private void moveTaskToDesktopInternal(TaskIdAttributeContainer taskContainer, + Runnable successCallback) { + if (mDesktopRecentsTransitionController == null) { + return; + } + mDesktopRecentsTransitionController.moveToDesktop(taskContainer.getTask().key.id); + successCallback.run(); + } + public interface TaskLaunchListener { void onTaskLaunched(); } diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index cec09824bf..3b31cd7b43 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -486,7 +486,11 @@ public class TaskView extends FrameLayout implements Reusable { return getItemInfo(mTask); } - protected WorkspaceItemInfo getItemInfo(@Nullable Task task) { + /** + * Builds proto for logging + */ + @VisibleForTesting + public WorkspaceItemInfo getItemInfo(@Nullable Task task) { WorkspaceItemInfo stubInfo = new WorkspaceItemInfo(); stubInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_TASK; stubInfo.container = LauncherSettings.Favorites.CONTAINER_TASKSWITCHER; diff --git a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt new file mode 100644 index 0000000000..b90839d5a8 --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.quickstep + +import android.content.ComponentName +import android.content.Intent +import android.platform.test.flag.junit.SetFlagsRule +import com.android.launcher3.AbstractFloatingView +import com.android.launcher3.AbstractFloatingViewHelper +import com.android.launcher3.Launcher +import com.android.launcher3.logging.StatsLogManager +import com.android.launcher3.logging.StatsLogManager.LauncherEvent +import com.android.launcher3.model.data.WorkspaceItemInfo +import com.android.launcher3.util.SplitConfigurationOptions +import com.android.quickstep.views.LauncherRecentsView +import com.android.quickstep.views.TaskView +import com.android.systemui.shared.recents.model.Task +import com.android.systemui.shared.recents.model.Task.TaskKey +import com.android.window.flags.Flags +import com.google.common.truth.Truth.assertThat +import org.junit.Rule +import org.junit.Test +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +/** Test for DesktopSystemShortcut */ +class DesktopSystemShortcutTest { + + @get:Rule val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT) + + private val launcher: Launcher = mock() + private val statsLogManager: StatsLogManager = mock() + private val statsLogger: StatsLogManager.StatsLogger = mock() + private val recentsView: LauncherRecentsView = mock() + private val taskView: TaskView = mock() + private val workspaceItemInfo: WorkspaceItemInfo = mock() + private val abstractFloatingViewHelper: AbstractFloatingViewHelper = mock() + private val factory: TaskShortcutFactory = + DesktopSystemShortcut.createFactory(abstractFloatingViewHelper) + + @Test + fun createDesktopTaskShortcutFactory_featureOff() { + setFlagsRule.disableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + + val task = + Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000)).apply { + isDockable = true + } + val taskContainer = + taskView.TaskIdAttributeContainer( + task, + null, + null, + SplitConfigurationOptions.STAGE_POSITION_UNDEFINED + ) + + val shortcuts = factory.getShortcuts(launcher, taskContainer) + assertThat(shortcuts).isNull() + } + + @Test + fun createDesktopTaskShortcutFactory_undockable() { + setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + + val task = + Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000)).apply { + isDockable = false + } + val taskContainer = + taskView.TaskIdAttributeContainer( + task, + null, + null, + SplitConfigurationOptions.STAGE_POSITION_UNDEFINED + ) + + val shortcuts = factory.getShortcuts(launcher, taskContainer) + assertThat(shortcuts).isNull() + } + + @Test + fun desktopSystemShortcutClicked() { + setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + + val task = + Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000)).apply { + isDockable = true + } + val taskContainer = + taskView.TaskIdAttributeContainer( + task, + null, + null, + SplitConfigurationOptions.STAGE_POSITION_UNDEFINED + ) + + whenever(launcher.getOverviewPanel()).thenReturn(recentsView) + whenever(launcher.statsLogManager).thenReturn(statsLogManager) + whenever(statsLogManager.logger()).thenReturn(statsLogger) + whenever(statsLogger.withItemInfo(any())).thenReturn(statsLogger) + whenever(taskView.getItemInfo(task)).thenReturn(workspaceItemInfo) + whenever(recentsView.moveTaskToDesktop(any(), any())).thenAnswer { + val successCallback = it.getArgument(1) + successCallback.run() + } + + val shortcuts = factory.getShortcuts(launcher, taskContainer) + assertThat(shortcuts).hasSize(1) + assertThat(shortcuts!!.first()).isInstanceOf(DesktopSystemShortcut::class.java) + + val desktopShortcut = shortcuts.first() as DesktopSystemShortcut + + desktopShortcut.onClick(taskView) + + val allTypesExceptRebindSafe = + AbstractFloatingView.TYPE_ALL and AbstractFloatingView.TYPE_REBIND_SAFE.inv() + verify(abstractFloatingViewHelper).closeOpenViews(launcher, true, allTypesExceptRebindSafe) + verify(recentsView).moveTaskToDesktop(eq(taskContainer), any()) + verify(statsLogger).withItemInfo(workspaceItemInfo) + verify(statsLogger).log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_DESKTOP_TAP) + } +} diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java index e7b88dc9b9..4ccf3dbdfb 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -278,18 +278,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch public static void closeOpenViews(ActivityContext activity, boolean animate, @FloatingViewType int type) { - BaseDragLayer dragLayer = activity.getDragLayer(); - // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer, - // and will be one of the last views. - for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) { - View child = dragLayer.getChildAt(i); - if (child instanceof AbstractFloatingView) { - AbstractFloatingView abs = (AbstractFloatingView) child; - if (abs.isOfType(type)) { - abs.close(animate); - } - } - } + new AbstractFloatingViewHelper().closeOpenViews(activity, animate, type); } public static void closeAllOpenViews(ActivityContext activity, boolean animate) { diff --git a/src/com/android/launcher3/AbstractFloatingViewHelper.kt b/src/com/android/launcher3/AbstractFloatingViewHelper.kt new file mode 100644 index 0000000000..0bfbc6e9fe --- /dev/null +++ b/src/com/android/launcher3/AbstractFloatingViewHelper.kt @@ -0,0 +1,38 @@ +/* + * 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.launcher3 + +import com.android.launcher3.AbstractFloatingView.FloatingViewType +import com.android.launcher3.views.ActivityContext + +/** + * Helper class for manaing AbstractFloatingViews which shows a floating UI on top of the launcher + * UI. + */ +class AbstractFloatingViewHelper { + fun closeOpenViews(activity: ActivityContext, animate: Boolean, @FloatingViewType type: Int) { + val dragLayer = activity.getDragLayer() + // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer, + // and will be one of the last views. + for (i in dragLayer.getChildCount() - 1 downTo 0) { + val child = dragLayer.getChildAt(i) + if (child is AbstractFloatingView && child.isOfType(type)) { + child.close(animate) + } + } + } +} diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java index e8f8ae25ea..441bbb579d 100644 --- a/src/com/android/launcher3/logging/StatsLogManager.java +++ b/src/com/android/launcher3/logging/StatsLogManager.java @@ -218,6 +218,9 @@ public class StatsLogManager implements ResourceBasedOverride { @UiEvent(doc = "User tapped on free form icon on a task menu.") LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP(519), + @UiEvent(doc = "User tapped on desktop icon on a task menu.") + LAUNCHER_SYSTEM_SHORTCUT_DESKTOP_TAP(1706), + @UiEvent(doc = "User tapped on pause app system shortcut.") LAUNCHER_SYSTEM_SHORTCUT_PAUSE_TAP(521), diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java index 0af7e677ed..bd32358035 100644 --- a/src/com/android/launcher3/popup/SystemShortcut.java +++ b/src/com/android/launcher3/popup/SystemShortcut.java @@ -22,6 +22,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.AbstractFloatingViewHelper; import com.android.launcher3.Flags; import com.android.launcher3.R; import com.android.launcher3.SecondaryDropTarget; @@ -61,23 +62,23 @@ public abstract class SystemShortcut extends ItemInfo protected final ItemInfo mItemInfo; protected final View mOriginalView; + private final AbstractFloatingViewHelper mAbstractFloatingViewHelper; + public SystemShortcut(int iconResId, int labelResId, T target, ItemInfo itemInfo, View originalView) { + this(iconResId, labelResId, target, itemInfo, originalView, + new AbstractFloatingViewHelper()); + } + + public SystemShortcut(int iconResId, int labelResId, T target, ItemInfo itemInfo, + View originalView, AbstractFloatingViewHelper abstractFloatingViewHelper) { mIconResId = iconResId; mLabelResId = labelResId; mAccessibilityActionId = labelResId; mTarget = target; mItemInfo = itemInfo; mOriginalView = originalView; - } - - public SystemShortcut(SystemShortcut other) { - mIconResId = other.mIconResId; - mLabelResId = other.mLabelResId; - mAccessibilityActionId = other.mAccessibilityActionId; - mTarget = other.mTarget; - mItemInfo = other.mItemInfo; - mOriginalView = other.mOriginalView; + mAbstractFloatingViewHelper = abstractFloatingViewHelper; } public void setIconAndLabelFor(View iconView, TextView labelView) { @@ -178,7 +179,7 @@ public abstract class SystemShortcut extends ItemInfo @Override public void onClick(View view) { - dismissTaskMenuView(mTarget); + dismissTaskMenuView(); Rect sourceBounds = Utilities.getViewBounds(view); new PackageManagerHelper(view.getContext()).startDetailsActivityForInfo( mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle()); @@ -327,7 +328,7 @@ public abstract class SystemShortcut extends ItemInfo @Override public void onClick(View view) { - dismissTaskMenuView(mTarget); + dismissTaskMenuView(); mTarget.getStatsLogManager().logger() .withItemInfo(mItemInfo) .log(LAUNCHER_SYSTEM_SHORTCUT_DONT_SUGGEST_APP_TAP); @@ -370,7 +371,7 @@ public abstract class SystemShortcut extends ItemInfo @Override public void onClick(View view) { - dismissTaskMenuView(mTarget); + dismissTaskMenuView(); SecondaryDropTarget.performUninstall(view.getContext(), mComponentName, mItemInfo); mTarget.getStatsLogManager() .logger() @@ -379,8 +380,8 @@ public abstract class SystemShortcut extends ItemInfo } } - public static void dismissTaskMenuView(T activity) { - AbstractFloatingView.closeOpenViews(activity, true, + protected void dismissTaskMenuView() { + mAbstractFloatingViewHelper.closeOpenViews(mTarget, true, AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE); } } diff --git a/tests/src/com/android/launcher3/AbstractFloatingViewHelperTest.kt b/tests/src/com/android/launcher3/AbstractFloatingViewHelperTest.kt new file mode 100644 index 0000000000..7ff544d9f7 --- /dev/null +++ b/tests/src/com/android/launcher3/AbstractFloatingViewHelperTest.kt @@ -0,0 +1,106 @@ +/* + * 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.launcher3 + +import android.view.View +import com.android.launcher3.dragndrop.DragLayer +import com.android.launcher3.views.ActivityContext +import org.junit.Before +import org.junit.Test +import org.mockito.kotlin.any +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyZeroInteractions +import org.mockito.kotlin.whenever + +/** Test for AbstractFloatingViewHelper */ +class AbstractFloatingViewHelperTest { + private val activityContext: ActivityContext = mock() + private val dragLayer: DragLayer = mock() + private val view: View = mock() + private val folderView: AbstractFloatingView = mock() + private val taskMenuView: AbstractFloatingView = mock() + private val abstractFloatingViewHelper = AbstractFloatingViewHelper() + + @Before + fun setup() { + whenever(activityContext.dragLayer).thenReturn(dragLayer) + whenever(dragLayer.childCount).thenReturn(3) + whenever(dragLayer.getChildAt(0)).thenReturn(view) + whenever(dragLayer.getChildAt(1)).thenReturn(folderView) + whenever(dragLayer.getChildAt(2)).thenReturn(taskMenuView) + whenever(folderView.isOfType(any())).thenAnswer { + (it.getArgument(0) and AbstractFloatingView.TYPE_FOLDER) != 0 + } + whenever(taskMenuView.isOfType(any())).thenAnswer { + (it.getArgument(0) and AbstractFloatingView.TYPE_TASK_MENU) != 0 + } + } + + @Test + fun closeOpenViews_all() { + abstractFloatingViewHelper.closeOpenViews( + activityContext, + true, + AbstractFloatingView.TYPE_ALL + ) + + verifyZeroInteractions(view) + verify(folderView).close(true) + verify(taskMenuView).close(true) + } + + @Test + fun closeOpenViews_taskMenu() { + abstractFloatingViewHelper.closeOpenViews( + activityContext, + true, + AbstractFloatingView.TYPE_TASK_MENU + ) + + verifyZeroInteractions(view) + verify(folderView, never()).close(any()) + verify(taskMenuView).close(true) + } + + @Test + fun closeOpenViews_other() { + abstractFloatingViewHelper.closeOpenViews( + activityContext, + true, + AbstractFloatingView.TYPE_PIN_IME_POPUP + ) + + verifyZeroInteractions(view) + verify(folderView, never()).close(any()) + verify(taskMenuView, never()).close(any()) + } + + @Test + fun closeOpenViews_both_animationOff() { + abstractFloatingViewHelper.closeOpenViews( + activityContext, + false, + AbstractFloatingView.TYPE_FOLDER or AbstractFloatingView.TYPE_TASK_MENU + ) + + verifyZeroInteractions(view) + verify(folderView).close(false) + verify(taskMenuView).close(false) + } +}