diff options
4 files changed, 166 insertions, 9 deletions
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index ee51ed020be6..a1804672da73 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4366,6 +4366,7 @@ <java-symbol type="dimen" name="seekbar_thumb_exclusion_max_size" /> <java-symbol type="layout" name="chooser_az_label_row" /> <java-symbol type="string" name="chooser_all_apps_button_label" /> + <java-symbol type="anim" name="resolver_close_anim" /> <java-symbol type="anim" name="resolver_launch_anim" /> <java-symbol type="style" name="Animation.DeviceDefault.Activity.Resolver" /> diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 6fa87592ccba..8c8975fdbdd5 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -94,6 +94,17 @@ flag { } 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 e1741c73d453..7c7efd0be8ed 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.View 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 @@ constructor( 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 @@ constructor( 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 000000000000..ac4107359dbc --- /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) + } +} |