Loading core/res/res/values/symbols.xml +1 −0 Original line number Original line Diff line number Diff line Loading @@ -4366,6 +4366,7 @@ <java-symbol type="dimen" name="seekbar_thumb_exclusion_max_size" /> <java-symbol type="dimen" name="seekbar_thumb_exclusion_max_size" /> <java-symbol type="layout" name="chooser_az_label_row" /> <java-symbol type="layout" name="chooser_az_label_row" /> <java-symbol type="string" name="chooser_all_apps_button_label" /> <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="anim" name="resolver_launch_anim" /> <java-symbol type="style" name="Animation.DeviceDefault.Activity.Resolver" /> <java-symbol type="style" name="Animation.DeviceDefault.Activity.Resolver" /> Loading packages/SystemUI/aconfig/systemui.aconfig +11 −0 Original line number Original line Diff line number Diff line Loading @@ -93,6 +93,17 @@ flag { bug: "308623704" bug: "308623704" } } 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 { flag { name: "notifications_background_media_icons" name: "notifications_background_media_icons" namespace: "systemui" namespace: "systemui" Loading packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt +25 −9 Original line number Original line Diff line number Diff line Loading @@ -26,12 +26,13 @@ import android.view.View import android.view.ViewGroup import android.view.ViewGroup import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView 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.MediaProjectionAppSelectorResultHandler import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope import com.android.systemui.mediaprojection.appselector.data.RecentTask import com.android.systemui.mediaprojection.appselector.data.RecentTask import com.android.systemui.mediaprojection.appselector.view.RecentTasksAdapter.RecentTaskClickListener import com.android.systemui.mediaprojection.appselector.view.RecentTasksAdapter.RecentTaskClickListener import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider.TaskPreviewSizeListener import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider.TaskPreviewSizeListener import com.android.systemui.res.R import com.android.systemui.util.recycler.HorizontalSpacerItemDecoration import com.android.systemui.util.recycler.HorizontalSpacerItemDecoration import javax.inject.Inject import javax.inject.Inject Loading Loading @@ -122,14 +123,7 @@ constructor( override fun onRecentAppClicked(task: RecentTask, view: View) { override fun onRecentAppClicked(task: RecentTask, view: View) { val launchCookie = LaunchCookie() val launchCookie = LaunchCookie() val activityOptions = val activityOptions = createAnimation(task, view) ActivityOptions.makeScaleUpAnimation( view, /* startX= */ 0, /* startY= */ 0, view.width, view.height ) activityOptions.pendingIntentBackgroundActivityStartMode = activityOptions.pendingIntentBackgroundActivityStartMode = MODE_BACKGROUND_ACTIVITY_START_ALLOWED MODE_BACKGROUND_ACTIVITY_START_ALLOWED activityOptions.setLaunchCookie(launchCookie) activityOptions.setLaunchCookie(launchCookie) Loading @@ -139,6 +133,28 @@ constructor( resultHandler.returnSelectedApp(launchCookie) 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) { override fun onTaskSizeChanged(size: Rect) { views?.recentsContainer?.setTaskHeightSize() views?.recentsContainer?.setTaskHeightSize() } } Loading packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt 0 → 100644 +129 −0 Original line number Original line Diff line number Diff line /* * 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) } } Loading
core/res/res/values/symbols.xml +1 −0 Original line number Original line Diff line number Diff line Loading @@ -4366,6 +4366,7 @@ <java-symbol type="dimen" name="seekbar_thumb_exclusion_max_size" /> <java-symbol type="dimen" name="seekbar_thumb_exclusion_max_size" /> <java-symbol type="layout" name="chooser_az_label_row" /> <java-symbol type="layout" name="chooser_az_label_row" /> <java-symbol type="string" name="chooser_all_apps_button_label" /> <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="anim" name="resolver_launch_anim" /> <java-symbol type="style" name="Animation.DeviceDefault.Activity.Resolver" /> <java-symbol type="style" name="Animation.DeviceDefault.Activity.Resolver" /> Loading
packages/SystemUI/aconfig/systemui.aconfig +11 −0 Original line number Original line Diff line number Diff line Loading @@ -93,6 +93,17 @@ flag { bug: "308623704" bug: "308623704" } } 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 { flag { name: "notifications_background_media_icons" name: "notifications_background_media_icons" namespace: "systemui" namespace: "systemui" Loading
packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt +25 −9 Original line number Original line Diff line number Diff line Loading @@ -26,12 +26,13 @@ import android.view.View import android.view.ViewGroup import android.view.ViewGroup import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView 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.MediaProjectionAppSelectorResultHandler import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope import com.android.systemui.mediaprojection.appselector.data.RecentTask import com.android.systemui.mediaprojection.appselector.data.RecentTask import com.android.systemui.mediaprojection.appselector.view.RecentTasksAdapter.RecentTaskClickListener import com.android.systemui.mediaprojection.appselector.view.RecentTasksAdapter.RecentTaskClickListener import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider.TaskPreviewSizeListener import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider.TaskPreviewSizeListener import com.android.systemui.res.R import com.android.systemui.util.recycler.HorizontalSpacerItemDecoration import com.android.systemui.util.recycler.HorizontalSpacerItemDecoration import javax.inject.Inject import javax.inject.Inject Loading Loading @@ -122,14 +123,7 @@ constructor( override fun onRecentAppClicked(task: RecentTask, view: View) { override fun onRecentAppClicked(task: RecentTask, view: View) { val launchCookie = LaunchCookie() val launchCookie = LaunchCookie() val activityOptions = val activityOptions = createAnimation(task, view) ActivityOptions.makeScaleUpAnimation( view, /* startX= */ 0, /* startY= */ 0, view.width, view.height ) activityOptions.pendingIntentBackgroundActivityStartMode = activityOptions.pendingIntentBackgroundActivityStartMode = MODE_BACKGROUND_ACTIVITY_START_ALLOWED MODE_BACKGROUND_ACTIVITY_START_ALLOWED activityOptions.setLaunchCookie(launchCookie) activityOptions.setLaunchCookie(launchCookie) Loading @@ -139,6 +133,28 @@ constructor( resultHandler.returnSelectedApp(launchCookie) 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) { override fun onTaskSizeChanged(size: Rect) { views?.recentsContainer?.setTaskHeightSize() views?.recentsContainer?.setTaskHeightSize() } } Loading
packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt 0 → 100644 +129 −0 Original line number Original line Diff line number Diff line /* * 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) } }