summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java37
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java51
-rw-r--r--packages/CredentialManager/res/values-fr/strings.xml4
-rw-r--r--packages/CredentialManager/res/values-hy/strings.xml6
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt102
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonViewModel.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModelTest.kt10
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java2
13 files changed, 209 insertions, 86 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/CredentialManager/res/values-fr/strings.xml b/packages/CredentialManager/res/values-fr/strings.xml
index 2a4ec4854908..47a97cc0258f 100644
--- a/packages/CredentialManager/res/values-fr/strings.xml
+++ b/packages/CredentialManager/res/values-fr/strings.xml
@@ -20,7 +20,7 @@
<string name="app_name" msgid="4539824758261855508">"Gestionnaire d\'identifiants"</string>
<string name="string_cancel" msgid="6369133483981306063">"Annuler"</string>
<string name="string_continue" msgid="1346732695941131882">"Continuer"</string>
- <string name="string_more_options" msgid="2763852250269945472">"Autre façon"</string>
+ <string name="string_more_options" msgid="2763852250269945472">"Autre option"</string>
<string name="string_learn_more" msgid="4541600451688392447">"En savoir plus"</string>
<string name="content_description_show_password" msgid="3283502010388521607">"Afficher le mot de passe"</string>
<string name="content_description_hide_password" msgid="6841375971631767996">"Masquer le mot de passe"</string>
@@ -40,7 +40,7 @@
<string name="choose_provider_title" msgid="8870795677024868108">"Choisissez où enregistrer vos <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Sélectionnez un gestionnaire de mots de passe pour enregistrer vos informations et vous connecter plus rapidement la prochaine fois"</string>
<string name="choose_create_option_passkey_title" msgid="7980430650778623135">"Créer une clé d\'accès pour se connecter à <xliff:g id="APPNAME">%1$s</xliff:g> ?"</string>
- <string name="choose_create_option_password_title" msgid="6238446571944651980">"Enregistrer un mot de passe pour se connecter à <xliff:g id="APPNAME">%1$s</xliff:g> ?"</string>
+ <string name="choose_create_option_password_title" msgid="6238446571944651980">"Enregistrer le mot de passe pour se connecter à <xliff:g id="APPNAME">%1$s</xliff:g> ?"</string>
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Enregistrer les informations de connexion pour <xliff:g id="APPNAME">%1$s</xliff:g> ?"</string>
<string name="passkey" msgid="632353688396759522">"clé d\'accès"</string>
<string name="password" msgid="6738570945182936667">"mot de passe"</string>
diff --git a/packages/CredentialManager/res/values-hy/strings.xml b/packages/CredentialManager/res/values-hy/strings.xml
index 6b6fd374d3f8..61b076a3ad5a 100644
--- a/packages/CredentialManager/res/values-hy/strings.xml
+++ b/packages/CredentialManager/res/values-hy/strings.xml
@@ -28,7 +28,7 @@
<string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Անցաբառերի շնորհիվ դուք բարդ գաղտնաբառեր ստեղծելու կամ հիշելու անհրաժեշտություն չեք ունենա"</string>
<string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Անցաբառերը գաղտնագրված թվային բանալիներ են, որոնք ստեղծվում են մատնահետքի, դեմքով ապակողպման կամ էկրանի կողպման օգտագործմամբ"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Դուք կարող եք մուտք գործել այլ սարքերում, քանի որ մուտքի բանալիները պահվում են գաղտնաբառերի կառավարիչում"</string>
- <string name="more_about_passkeys_title" msgid="7797903098728837795">"Ավելին՝ անցաբառերի մասին"</string>
+ <string name="more_about_passkeys_title" msgid="7797903098728837795">"Ավելին՝ մուտքի բանալիների մասին"</string>
<string name="passwordless_technology_title" msgid="2497513482056606668">"Գաղտնաբառեր չպահանջող տեխնոլոգիա"</string>
<string name="passwordless_technology_detail" msgid="6853928846532955882">"Մուտքի բանալիները ձեզ թույլ են տալիս մուտք գործել առանց գաղտնաբառերի։ Ձեզ պարզապես հարկավոր է օգտագործել ձեր մատնահետքը, դիմաճանաչումը, PIN կոդը կամ նախշը՝ ձեր ինքնությունը հաստատելու և մուտքի բանալի ստեղծելու համար։"</string>
<string name="public_key_cryptography_title" msgid="6751970819265298039">"Բաց բանալու կրիպտոգրաֆիա"</string>
@@ -36,7 +36,7 @@
<string name="improved_account_security_title" msgid="1069841917893513424">"Հաշվի բարելավված անվտանգություն"</string>
<string name="improved_account_security_detail" msgid="9123750251551844860">"Յուրաքանչյուր բանալի բացառապես կապված է հավելվածի կամ կայքի հետ, որի համար այն ստեղծվել է, ուստի դուք երբեք չեք կարող սխալմամբ մուտք գործել կեղծ հավելված կամ կայք։ Բացի այդ՝ սերվերներում պահվում են միայն բաց բանալիներ, ինչը զգալիորեն դժվարացնում է կոտրումը։"</string>
<string name="seamless_transition_title" msgid="5335622196351371961">"Սահուն անցում"</string>
- <string name="seamless_transition_detail" msgid="4475509237171739843">"Թեև մենք առանց գաղտնաբառերի ապագայի ճանապարհին ենք, դրանք դեռ հասանելի կլինեն անցաբառերի հետ մեկտեղ։"</string>
+ <string name="seamless_transition_detail" msgid="4475509237171739843">"Թեև մենք առանց գաղտնաբառերի ապագայի ճանապարհին ենք, դրանք դեռ հասանելի կլինեն մուտքի բանալիների հետ մեկտեղ։"</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Նշեք, թե որտեղ եք ուզում պահել ձեր <xliff:g id="CREATETYPES">%1$s</xliff:g>ը"</string>
<string name="choose_provider_body" msgid="4967074531845147434">"Ընտրեք գաղտնաբառերի կառավարիչ՝ ձեր տեղեկությունները պահելու և հաջորդ անգամ ավելի արագ մուտք գործելու համար"</string>
<string name="choose_create_option_passkey_title" msgid="7980430650778623135">"Ստեղծե՞լ մուտքի բանալի՝ <xliff:g id="APPNAME">%1$s</xliff:g> հավելված մուտք գործելու համար"</string>
@@ -44,7 +44,7 @@
<string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"Պահե՞լ «<xliff:g id="APPNAME">%1$s</xliff:g>» հավելվածի մուտքի տվյալները"</string>
<string name="passkey" msgid="632353688396759522">"մուտքի բանալի"</string>
<string name="password" msgid="6738570945182936667">"գաղտնաբառ"</string>
- <string name="passkeys" msgid="5733880786866559847">"անցաբառեր"</string>
+ <string name="passkeys" msgid="5733880786866559847">"մուտքի բանալիներ"</string>
<string name="passwords" msgid="5419394230391253816">"գաղտնաբառեր"</string>
<string name="sign_ins" msgid="4710739369149469208">"մուտք"</string>
<string name="sign_in_info" msgid="2627704710674232328">"մուտքի տվյալներ"</string>
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) {