[Partial Screen Sharing] Fix app selector not animating out when the selected task is the foreground task
Test: MediaProjectionRecentsViewControllerTest
Test: Manual
Fixes: 314385883
Flag: ACONFIG com.android.systemui.pss_app_selector_abrupt_exit_fix DISABLED
Change-Id: Ic4a8f65c168a8a963502da8e71617732968e25a8
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index f5c4843..f4561a1 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -84,6 +84,17 @@
}
flag {
+ name: "pss_app_selector_abrupt_exit_fix"
+ namespace: "systemui"
+ description: "Fixes the app selector abruptly disappearing without an animation, when the"
+ "selected task is the foreground task."
+ bug: "314385883"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "notifications_background_media_icons"
namespace: "systemui"
description: "Updates icons for media notifications in the background."
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
index a811065..75ba092 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
@@ -26,12 +26,13 @@
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import com.android.systemui.res.R
+import com.android.systemui.Flags.pssAppSelectorAbruptExitFix
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorResultHandler
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import com.android.systemui.mediaprojection.appselector.view.RecentTasksAdapter.RecentTaskClickListener
import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider.TaskPreviewSizeListener
+import com.android.systemui.res.R
import com.android.systemui.util.recycler.HorizontalSpacerItemDecoration
import javax.inject.Inject
@@ -122,14 +123,7 @@
override fun onRecentAppClicked(task: RecentTask, view: View) {
val launchCookie = LaunchCookie()
- val activityOptions =
- ActivityOptions.makeScaleUpAnimation(
- view,
- /* startX= */ 0,
- /* startY= */ 0,
- view.width,
- view.height
- )
+ val activityOptions = createAnimation(task, view)
activityOptions.pendingIntentBackgroundActivityStartMode =
MODE_BACKGROUND_ACTIVITY_START_ALLOWED
activityOptions.setLaunchCookie(launchCookie)
@@ -139,6 +133,28 @@
resultHandler.returnSelectedApp(launchCookie)
}
+ private fun createAnimation(task: RecentTask, view: View): ActivityOptions =
+ if (pssAppSelectorAbruptExitFix() && task.isForegroundTask) {
+ // When the selected task is in the foreground, the scale up animation doesn't work.
+ // We fallback to the default close animation.
+ ActivityOptions.makeCustomTaskAnimation(
+ view.context,
+ /* enterResId= */ 0,
+ /* exitResId= */ com.android.internal.R.anim.resolver_close_anim,
+ /* handler = */ null,
+ /* startedListener = */ null,
+ /* finishedListener = */ null
+ )
+ } else {
+ ActivityOptions.makeScaleUpAnimation(
+ view,
+ /* startX= */ 0,
+ /* startY= */ 0,
+ view.width,
+ view.height
+ )
+ }
+
override fun onTaskSizeChanged(size: Rect) {
views?.recentsContainer?.setTaskHeightSize()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt
new file mode 100644
index 0000000..ac41073
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt
@@ -0,0 +1,129 @@
+/*
+ * 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.systemui.mediaprojection.appselector.view
+
+import android.app.ActivityOptions
+import android.app.IActivityTaskManager
+import android.os.Bundle
+import android.view.View
+import android.view.ViewGroup
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_PSS_APP_SELECTOR_ABRUPT_EXIT_FIX
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorResultHandler
+import com.android.systemui.mediaprojection.appselector.data.RecentTask
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Expect
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.any
+import org.mockito.Mockito.verify
+
+@SmallTest
+class MediaProjectionRecentsViewControllerTest : SysuiTestCase() {
+
+ @get:Rule val expect: Expect = Expect.create()
+
+ private val recentTasksAdapter = mock<RecentTasksAdapter>()
+ private val tasksAdapterFactory = RecentTasksAdapter.Factory { _, _ -> recentTasksAdapter }
+ private val taskViewSizeProvider = mock<TaskPreviewSizeProvider>()
+ private val activityTaskManager = mock<IActivityTaskManager>()
+ private val resultHandler = mock<MediaProjectionAppSelectorResultHandler>()
+ private val bundleCaptor = ArgumentCaptor.forClass(Bundle::class.java)
+
+ private val task =
+ RecentTask(
+ taskId = 123,
+ displayId = 456,
+ userId = 789,
+ topActivityComponent = null,
+ baseIntentComponent = null,
+ colorBackground = null,
+ isForegroundTask = false
+ )
+
+ private val taskView =
+ View(context).apply {
+ layoutParams = ViewGroup.LayoutParams(/* width = */ 100, /* height = */ 200)
+ }
+
+ private val controller =
+ MediaProjectionRecentsViewController(
+ tasksAdapterFactory,
+ taskViewSizeProvider,
+ activityTaskManager,
+ resultHandler
+ )
+
+ @Test
+ fun onRecentAppClicked_taskWithSameIdIsStartedFromRecents() {
+ controller.onRecentAppClicked(task, taskView)
+
+ verify(activityTaskManager).startActivityFromRecents(eq(task.taskId), any())
+ }
+
+ @Test
+ fun onRecentAppClicked_launchDisplayIdIsSet() {
+ controller.onRecentAppClicked(task, taskView)
+
+ assertThat(getStartedTaskActivityOptions().launchDisplayId).isEqualTo(task.displayId)
+ }
+
+ @Test
+ fun onRecentAppClicked_taskNotInForeground_usesScaleUpAnimation() {
+ controller.onRecentAppClicked(task, taskView)
+
+ assertThat(getStartedTaskActivityOptions().animationType)
+ .isEqualTo(ActivityOptions.ANIM_SCALE_UP)
+ }
+
+ @Test
+ fun onRecentAppClicked_taskInForeground_flagOff_usesScaleUpAnimation() {
+ mSetFlagsRule.disableFlags(FLAG_PSS_APP_SELECTOR_ABRUPT_EXIT_FIX)
+
+ controller.onRecentAppClicked(task, taskView)
+
+ assertThat(getStartedTaskActivityOptions().animationType)
+ .isEqualTo(ActivityOptions.ANIM_SCALE_UP)
+ }
+
+ @Test
+ fun onRecentAppClicked_taskInForeground_flagOn_usesDefaultAnimation() {
+ mSetFlagsRule.enableFlags(FLAG_PSS_APP_SELECTOR_ABRUPT_EXIT_FIX)
+ val foregroundTask = task.copy(isForegroundTask = true)
+
+ controller.onRecentAppClicked(foregroundTask, taskView)
+
+ expect
+ .that(getStartedTaskActivityOptions().animationType)
+ .isEqualTo(ActivityOptions.ANIM_CUSTOM)
+ expect.that(getStartedTaskActivityOptions().overrideTaskTransition).isTrue()
+ expect
+ .that(getStartedTaskActivityOptions().customExitResId)
+ .isEqualTo(com.android.internal.R.anim.resolver_close_anim)
+ expect.that(getStartedTaskActivityOptions().customEnterResId).isEqualTo(0)
+ }
+
+ private fun getStartedTaskActivityOptions(): ActivityOptions {
+ verify(activityTaskManager)
+ .startActivityFromRecents(eq(task.taskId), bundleCaptor.capture())
+ return ActivityOptions.fromBundle(bundleCaptor.value)
+ }
+}