diff options
11 files changed, 204 insertions, 81 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java index fa2e23647a39..f989991ab004 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java @@ -194,6 +194,10 @@ public class CompatUIConfiguration implements DeviceConfig.OnPropertiesChangedLi return mHideSizeCompatRestartButtonTolerance; } + int getDefaultHideRestartButtonTolerance() { + return MAX_PERCENTAGE_VAL; + } + boolean getHasSeenLetterboxEducation(int userId) { return mLetterboxEduSharedPreferences .getBoolean(dontShowLetterboxEduKey(userId), /* default= */ false); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java index dbf7186def8a..15c6cbc3f1c4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java @@ -20,11 +20,11 @@ import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED; import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN; import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED; import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED; +import static android.view.WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP; import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.AppCompatTaskInfo; import android.app.AppCompatTaskInfo.CameraCompatControlState; import android.app.TaskInfo; import android.content.Context; @@ -217,14 +217,30 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract { @VisibleForTesting boolean shouldShowSizeCompatRestartButton(@NonNull TaskInfo taskInfo) { - if (!Flags.allowHideScmButton()) { + // Always show button if display is phone sized. + if (!Flags.allowHideScmButton() || taskInfo.configuration.smallestScreenWidthDp + < LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP) { return true; } - final AppCompatTaskInfo appCompatTaskInfo = taskInfo.appCompatTaskInfo; - final Rect taskBounds = taskInfo.configuration.windowConfiguration.getBounds(); - final int letterboxArea = computeArea(appCompatTaskInfo.topActivityLetterboxWidth, - appCompatTaskInfo.topActivityLetterboxHeight); - final int taskArea = computeArea(taskBounds.width(), taskBounds.height()); + + final int letterboxWidth = taskInfo.appCompatTaskInfo.topActivityLetterboxWidth; + final int letterboxHeight = taskInfo.appCompatTaskInfo.topActivityLetterboxHeight; + final Rect stableBounds = getTaskStableBounds(); + final int appWidth = stableBounds.width(); + final int appHeight = stableBounds.height(); + // App is floating, should always show restart button. + if (appWidth > letterboxWidth && appHeight > letterboxHeight) { + return true; + } + // If app fills the width of the display, don't show restart button (for landscape apps) + // if device has a custom tolerance value. + if (mHideScmTolerance != mCompatUIConfiguration.getDefaultHideRestartButtonTolerance() + && appWidth == letterboxWidth) { + return false; + } + + final int letterboxArea = letterboxWidth * letterboxHeight; + final int taskArea = appWidth * appHeight; if (letterboxArea == 0 || taskArea == 0) { return false; } @@ -232,13 +248,6 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract { return percentageAreaOfLetterboxInTask < mHideScmTolerance; } - private int computeArea(int width, int height) { - if (width == 0 || height == 0) { - return 0; - } - return width * height; - } - private void updateVisibilityOfViews() { if (mLayout == null) { return; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java index 4f261cd79d39..d92e5aa0890a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java @@ -22,6 +22,7 @@ import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPL import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED; import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; import static android.view.WindowInsets.Type.navigationBars; +import static android.view.WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; @@ -98,14 +99,28 @@ public class CompatUIWindowManagerTest extends ShellTestCase { private CompatUIWindowManager mWindowManager; private TaskInfo mTaskInfo; + private DisplayLayout mDisplayLayout; @Before public void setUp() { MockitoAnnotations.initMocks(this); doReturn(100).when(mCompatUIConfiguration).getHideSizeCompatRestartButtonTolerance(); mTaskInfo = createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN); + + final DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.logicalWidth = TASK_WIDTH; + displayInfo.logicalHeight = TASK_HEIGHT; + mDisplayLayout = new DisplayLayout(displayInfo, + mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false); + final InsetsState insetsState = new InsetsState(); + insetsState.setDisplayFrame(new Rect(0, 0, TASK_WIDTH, TASK_HEIGHT)); + final InsetsSource insetsSource = new InsetsSource( + InsetsSource.createId(null, 0, navigationBars()), navigationBars()); + insetsSource.setFrame(0, TASK_HEIGHT - 200, TASK_WIDTH, TASK_HEIGHT); + insetsState.addSource(insetsSource); + mDisplayLayout.setInsets(mContext.getResources(), insetsState); mWindowManager = new CompatUIWindowManager(mContext, mTaskInfo, mSyncTransactionQueue, - mCallback, mTaskListener, new DisplayLayout(), new CompatUIHintsState(), + mCallback, mTaskListener, mDisplayLayout, new CompatUIHintsState(), mCompatUIConfiguration, mOnRestartButtonClicked); spyOn(mWindowManager); @@ -363,9 +378,9 @@ public class CompatUIWindowManagerTest extends ShellTestCase { // Update if the insets change on the existing display layout clearInvocations(mWindowManager); - InsetsState insetsState = new InsetsState(); + final InsetsState insetsState = new InsetsState(); insetsState.setDisplayFrame(new Rect(0, 0, 1000, 2000)); - InsetsSource insetsSource = new InsetsSource( + final InsetsSource insetsSource = new InsetsSource( InsetsSource.createId(null, 0, navigationBars()), navigationBars()); insetsSource.setFrame(0, 1800, 1000, 2000); insetsState.addSource(insetsSource); @@ -493,16 +508,14 @@ public class CompatUIWindowManagerTest extends ShellTestCase { @Test public void testShouldShowSizeCompatRestartButton() { mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_HIDE_SCM_BUTTON); - - doReturn(86).when(mCompatUIConfiguration).getHideSizeCompatRestartButtonTolerance(); + doReturn(85).when(mCompatUIConfiguration).getHideSizeCompatRestartButtonTolerance(); mWindowManager = new CompatUIWindowManager(mContext, mTaskInfo, mSyncTransactionQueue, - mCallback, mTaskListener, new DisplayLayout(), new CompatUIHintsState(), + mCallback, mTaskListener, mDisplayLayout, new CompatUIHintsState(), mCompatUIConfiguration, mOnRestartButtonClicked); // Simulate rotation of activity in square display TaskInfo taskInfo = createTaskInfo(true, CAMERA_COMPAT_CONTROL_HIDDEN); - taskInfo.configuration.windowConfiguration.setBounds(new Rect(0, 0, 2000, 2000)); - taskInfo.appCompatTaskInfo.topActivityLetterboxHeight = 2000; + taskInfo.appCompatTaskInfo.topActivityLetterboxHeight = TASK_HEIGHT; taskInfo.appCompatTaskInfo.topActivityLetterboxWidth = 1850; assertFalse(mWindowManager.shouldShowSizeCompatRestartButton(taskInfo)); @@ -512,11 +525,21 @@ public class CompatUIWindowManagerTest extends ShellTestCase { assertTrue(mWindowManager.shouldShowSizeCompatRestartButton(taskInfo)); // Simulate folding - taskInfo.configuration.windowConfiguration.setBounds(new Rect(0, 0, 1000, 2000)); - assertFalse(mWindowManager.shouldShowSizeCompatRestartButton(taskInfo)); + final InsetsState insetsState = new InsetsState(); + insetsState.setDisplayFrame(new Rect(0, 0, 1000, TASK_HEIGHT)); + final InsetsSource insetsSource = new InsetsSource( + InsetsSource.createId(null, 0, navigationBars()), navigationBars()); + insetsSource.setFrame(0, TASK_HEIGHT - 200, 1000, TASK_HEIGHT); + insetsState.addSource(insetsSource); + mDisplayLayout.setInsets(mContext.getResources(), insetsState); + mWindowManager.updateDisplayLayout(mDisplayLayout); + taskInfo.configuration.smallestScreenWidthDp = LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP - 100; + assertTrue(mWindowManager.shouldShowSizeCompatRestartButton(taskInfo)); - taskInfo.appCompatTaskInfo.topActivityLetterboxWidth = 1000; - taskInfo.appCompatTaskInfo.topActivityLetterboxHeight = 500; + // Simulate floating app with 90& area, more than tolerance + taskInfo.configuration.smallestScreenWidthDp = LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP; + taskInfo.appCompatTaskInfo.topActivityLetterboxWidth = 950; + taskInfo.appCompatTaskInfo.topActivityLetterboxHeight = 1900; assertTrue(mWindowManager.shouldShowSizeCompatRestartButton(taskInfo)); } @@ -528,10 +551,10 @@ public class CompatUIWindowManagerTest extends ShellTestCase { taskInfo.appCompatTaskInfo.cameraCompatControlState = cameraCompatControlState; taskInfo.configuration.uiMode &= ~Configuration.UI_MODE_TYPE_DESK; // Letterboxed activity that takes half the screen should show size compat restart button - taskInfo.configuration.windowConfiguration.setBounds( - new Rect(0, 0, TASK_WIDTH, TASK_HEIGHT)); taskInfo.appCompatTaskInfo.topActivityLetterboxHeight = 1000; taskInfo.appCompatTaskInfo.topActivityLetterboxWidth = 1000; + // Screen width dp larger than a normal phone. + taskInfo.configuration.smallestScreenWidthDp = LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP; return taskInfo; } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt index 07e143a34319..ef1d87da47be 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt @@ -87,7 +87,8 @@ constructor( AppCompatResources.getDrawable(context, R.drawable.ic_screenshot_edit), context.resources.getString(R.string.screenshot_edit_label), context.resources.getString(R.string.screenshot_edit_description), - ) + ), + showDuringEntrance = true, ) { debugLog(LogConfig.DEBUG_ACTIONS) { "Edit tapped" } uiEventLogger.log(SCREENSHOT_EDIT_TAPPED, 0, request.packageNameString) @@ -105,7 +106,8 @@ constructor( AppCompatResources.getDrawable(context, R.drawable.ic_screenshot_share), context.resources.getString(R.string.screenshot_share_label), context.resources.getString(R.string.screenshot_share_description), - ) + ), + showDuringEntrance = true, ) { debugLog(LogConfig.DEBUG_ACTIONS) { "Share tapped" } uiEventLogger.log(SCREENSHOT_SHARE_TAPPED, 0, request.packageNameString) @@ -125,7 +127,8 @@ constructor( AppCompatResources.getDrawable(context, R.drawable.ic_screenshot_scroll), context.resources.getString(R.string.screenshot_scroll_label), context.resources.getString(R.string.screenshot_scroll_label), - ) + ), + showDuringEntrance = true, ) { onClick.run() } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt index 9b5e71827ead..412b08905ae9 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt @@ -45,6 +45,7 @@ import com.android.systemui.screenshot.scroll.ScrollCaptureController import com.android.systemui.screenshot.ui.ScreenshotAnimationController import com.android.systemui.screenshot.ui.ScreenshotShelfView import com.android.systemui.screenshot.ui.binder.ScreenshotShelfViewBinder +import com.android.systemui.screenshot.ui.viewmodel.AnimationState import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -119,12 +120,19 @@ constructor( override fun updateOrientation(insets: WindowInsets) {} override fun createScreenshotDropInAnimation(screenRect: Rect, showFlash: Boolean): Animator { - val entrance = animationController.getEntranceAnimation(screenRect, showFlash) - entrance.doOnStart { thumbnailObserver.onEntranceStarted() } + val entrance = + animationController.getEntranceAnimation(screenRect, showFlash) { + viewModel.setAnimationState(AnimationState.ENTRANCE_REVEAL) + } + entrance.doOnStart { + thumbnailObserver.onEntranceStarted() + viewModel.setAnimationState(AnimationState.ENTRANCE_STARTED) + } entrance.doOnEnd { // reset the timeout when animation finishes callbacks?.onUserInteraction() thumbnailObserver.onEntranceComplete() + viewModel.setAnimationState(AnimationState.ENTRANCE_COMPLETE) } return entrance } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt index da268300e8c4..06e88f46c5f1 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt @@ -47,7 +47,11 @@ class ScreenshotAnimationController(private val view: ScreenshotShelfView) { view.requireViewById(R.id.screenshot_dismiss_button) ) - fun getEntranceAnimation(bounds: Rect, showFlash: Boolean): Animator { + fun getEntranceAnimation( + bounds: Rect, + showFlash: Boolean, + onRevealMilestone: () -> Unit + ): Animator { val entranceAnimation = AnimatorSet() val previewAnimator = getPreviewAnimator(bounds) @@ -70,7 +74,19 @@ class ScreenshotAnimationController(private val view: ScreenshotShelfView) { entranceAnimation.doOnStart { screenshotPreview.visibility = View.INVISIBLE } } - entranceAnimation.play(getActionsAnimator()).with(previewAnimator) + val actionsAnimator = getActionsAnimator() + entranceAnimation.play(actionsAnimator).with(previewAnimator) + + // This isn't actually animating anything but is basically a timer for the first 200ms of + // the entrance animation. Using an animator here ensures that this is scaled if we change + // animator duration scales. + val revealMilestoneAnimator = + ValueAnimator.ofFloat(0f).apply { + duration = 0 + startDelay = ACTION_REVEAL_DELAY_MS + doOnEnd { onRevealMilestone() } + } + entranceAnimation.play(revealMilestoneAnimator).with(actionsAnimator) val fadeInAnimator = ValueAnimator.ofFloat(0f, 1f) fadeInAnimator.addUpdateListener { @@ -198,5 +214,6 @@ class ScreenshotAnimationController(private val view: ScreenshotShelfView) { private const val FLASH_OUT_DURATION_MS: Long = 217 private const val PREVIEW_X_ANIMATION_DURATION_MS: Long = 234 private const val PREVIEW_Y_ANIMATION_DURATION_MS: Long = 500 + private const val ACTION_REVEAL_DELAY_MS: Long = 200 } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt index bc35e6b17345..43c0107c12d5 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt @@ -31,6 +31,8 @@ import com.android.systemui.res.R import com.android.systemui.screenshot.ScreenshotEvent import com.android.systemui.screenshot.ui.ScreenshotShelfView import com.android.systemui.screenshot.ui.SwipeGestureListener +import com.android.systemui.screenshot.ui.viewmodel.ActionButtonViewModel +import com.android.systemui.screenshot.ui.viewmodel.AnimationState import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel import com.android.systemui.util.children import kotlinx.coroutines.Dispatchers @@ -59,7 +61,6 @@ object ScreenshotShelfViewBinder { val previewBorder = view.requireViewById<View>(R.id.screenshot_preview_border) previewView.clipToOutline = true previewViewBlur.clipToOutline = true - val actionsContainer: LinearLayout = view.requireViewById(R.id.screenshot_actions) val dismissButton = view.requireViewById<View>(R.id.screenshot_dismiss_button) dismissButton.visibility = if (viewModel.showDismissButton) View.VISIBLE else View.GONE dismissButton.setOnClickListener { @@ -90,47 +91,72 @@ object ScreenshotShelfViewBinder { } launch { viewModel.actions.collect { actions -> - val visibleActions = actions.filter { it.visible } + updateActions( + actions, + viewModel.animationState.value, + view, + layoutInflater + ) + } + } + launch { + viewModel.animationState.collect { animationState -> + updateActions( + viewModel.actions.value, + animationState, + view, + layoutInflater + ) + } + } + } + } + } + } - if (visibleActions.isNotEmpty()) { - view - .requireViewById<View>(R.id.actions_container_background) - .visibility = View.VISIBLE - } + private fun updateActions( + actions: List<ActionButtonViewModel>, + animationState: AnimationState, + view: ScreenshotShelfView, + layoutInflater: LayoutInflater + ) { + val actionsContainer: LinearLayout = view.requireViewById(R.id.screenshot_actions) + val visibleActions = + actions.filter { + it.visible && + (animationState == AnimationState.ENTRANCE_COMPLETE || + animationState == AnimationState.ENTRANCE_REVEAL || + it.showDuringEntrance) + } - // Remove any buttons not in the new list, then do another pass to add - // any new actions and update any that are already there. - // This assumes that actions can never change order and that each action - // ID is unique. - val newIds = visibleActions.map { it.id } + if (visibleActions.isNotEmpty()) { + view.requireViewById<View>(R.id.actions_container_background).visibility = View.VISIBLE + } - for (child in actionsContainer.children.toList()) { - if (child.tag !in newIds) { - actionsContainer.removeView(child) - } - } + // Remove any buttons not in the new list, then do another pass to add + // any new actions and update any that are already there. + // This assumes that actions can never change order and that each action + // ID is unique. + val newIds = visibleActions.map { it.id } - for ((index, action) in visibleActions.withIndex()) { - val currentView: View? = actionsContainer.getChildAt(index) - if (action.id == currentView?.tag) { - // Same ID, update the display - ActionButtonViewBinder.bind(currentView, action) - } else { - // Different ID. Removals have already happened so this must - // mean that the new action must be inserted here. - val actionButton = - layoutInflater.inflate( - R.layout.shelf_action_chip, - actionsContainer, - false - ) - actionsContainer.addView(actionButton, index) - ActionButtonViewBinder.bind(actionButton, action) - } - } - } - } - } + for (child in actionsContainer.children.toList()) { + if (child.tag !in newIds) { + actionsContainer.removeView(child) + } + } + + for ((index, action) in visibleActions.withIndex()) { + val currentView: View? = actionsContainer.getChildAt(index) + if (action.id == currentView?.tag) { + // Same ID, update the display + ActionButtonViewBinder.bind(currentView, action) + } else { + // Different ID. Removals have already happened so this must + // mean that the new action must be inserted here. + val actionButton = + layoutInflater.inflate(R.layout.shelf_action_chip, actionsContainer, false) + actionsContainer.addView(actionButton, index) + ActionButtonViewBinder.bind(actionButton, action) } } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonViewModel.kt index c5fa8db953fa..364ab7624b5e 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonViewModel.kt @@ -20,6 +20,7 @@ data class ActionButtonViewModel( val appearance: ActionButtonAppearance, val id: Int, val visible: Boolean, + val showDuringEntrance: Boolean, val onClicked: (() -> Unit)?, ) { companion object { @@ -29,7 +30,14 @@ data class ActionButtonViewModel( fun withNextId( appearance: ActionButtonAppearance, + showDuringEntrance: Boolean, onClicked: (() -> Unit)? - ): ActionButtonViewModel = ActionButtonViewModel(appearance, getId(), true, onClicked) + ): ActionButtonViewModel = + ActionButtonViewModel(appearance, getId(), true, showDuringEntrance, onClicked) + + fun withNextId( + appearance: ActionButtonAppearance, + onClicked: (() -> Unit)? + ): ActionButtonViewModel = withNextId(appearance, showDuringEntrance = true, onClicked) } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt index f67ad402a738..5f36f73f2135 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt @@ -29,6 +29,9 @@ class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager val previewAction: StateFlow<(() -> Unit)?> = _previewAction private val _actions = MutableStateFlow(emptyList<ActionButtonViewModel>()) val actions: StateFlow<List<ActionButtonViewModel>> = _actions + private val _animationState = MutableStateFlow(AnimationState.NOT_STARTED) + val animationState: StateFlow<AnimationState> = _animationState + val showDismissButton: Boolean get() = accessibilityManager.isEnabled @@ -40,9 +43,14 @@ class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager _previewAction.value = onClick } - fun addAction(actionAppearance: ActionButtonAppearance, onClicked: (() -> Unit)): Int { + fun addAction( + actionAppearance: ActionButtonAppearance, + showDuringEntrance: Boolean, + onClicked: (() -> Unit) + ): Int { val actionList = _actions.value.toMutableList() - val action = ActionButtonViewModel.withNextId(actionAppearance, onClicked) + val action = + ActionButtonViewModel.withNextId(actionAppearance, showDuringEntrance, onClicked) actionList.add(action) _actions.value = actionList return action.id @@ -57,6 +65,7 @@ class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager actionList[index].appearance, actionId, visible, + actionList[index].showDuringEntrance, actionList[index].onClicked ) _actions.value = actionList @@ -74,6 +83,7 @@ class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager appearance, actionId, actionList[index].visible, + actionList[index].showDuringEntrance, actionList[index].onClicked ) _actions.value = actionList @@ -92,13 +102,26 @@ class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager } } + // TODO: this should be handled entirely within the view binder. + fun setAnimationState(state: AnimationState) { + _animationState.value = state + } + fun reset() { _preview.value = null _previewAction.value = null _actions.value = listOf() + _animationState.value = AnimationState.NOT_STARTED } companion object { const val TAG = "ScreenshotViewModel" } } + +enum class AnimationState { + NOT_STARTED, + ENTRANCE_STARTED, // The first 200ms of the entrance animation + ENTRANCE_REVEAL, // The rest of the entrance animation + ENTRANCE_COMPLETE, +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModelTest.kt index d44e26c266fc..e32086b79918 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModelTest.kt @@ -34,20 +34,21 @@ class ScreenshotViewModelTest { assertThat(viewModel.actions.value).isEmpty() - viewModel.addAction(appearance, onclick) + viewModel.addAction(appearance, true, onclick) assertThat(viewModel.actions.value).hasSize(1) val added = viewModel.actions.value[0] assertThat(added.appearance).isEqualTo(appearance) assertThat(added.onClicked).isEqualTo(onclick) + assertThat(added.showDuringEntrance).isTrue() } @Test fun testRemoveAction() { val viewModel = ScreenshotViewModel(accessibilityManager) - val firstId = viewModel.addAction(ActionButtonAppearance(null, "", ""), {}) - val secondId = viewModel.addAction(appearance, onclick) + val firstId = viewModel.addAction(ActionButtonAppearance(null, "", ""), false, {}) + val secondId = viewModel.addAction(appearance, false, onclick) assertThat(viewModel.actions.value).hasSize(2) assertThat(firstId).isNotEqualTo(secondId) @@ -58,13 +59,14 @@ class ScreenshotViewModelTest { val remaining = viewModel.actions.value[0] assertThat(remaining.appearance).isEqualTo(appearance) + assertThat(remaining.showDuringEntrance).isFalse() assertThat(remaining.onClicked).isEqualTo(onclick) } @Test fun testUpdateActionAppearance() { val viewModel = ScreenshotViewModel(accessibilityManager) - val id = viewModel.addAction(appearance, onclick) + val id = viewModel.addAction(appearance, false, onclick) val otherAppearance = ActionButtonAppearance(null, "Other", "Other") viewModel.updateActionAppearance(id, otherAppearance) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 229bb9e1951c..3bb898042a67 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -7028,7 +7028,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // by finishing the recents animation and moving it to top. That also avoids flickering // due to wait for previous activity to be paused if it supports PiP that ignores the // effect of resume-while-pausing. - if (r == null || r == mAnimatingRecents) { + if (r == null || r == mAnimatingRecents || r.getDisplayId() != mDisplayId) { return; } if (mAnimatingRecents != null && mRecentsWillBeTop) { |