diff options
4 files changed, 127 insertions, 1 deletions
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index a74c787fdab1..15c40c0fa3a9 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2562,6 +2562,8 @@ <java-symbol type="string" name="zen_mode_default_weekends_name" /> <java-symbol type="string" name="zen_mode_default_events_name" /> <java-symbol type="string" name="zen_mode_default_every_night_name" /> + <java-symbol type="string" name="display_rotation_camera_compat_toast_after_rotation" /> + <java-symbol type="string" name="display_rotation_camera_compat_toast_in_split_screen" /> <java-symbol type="array" name="config_system_condition_providers" /> <java-symbol type="string" name="muted_by" /> <java-symbol type="string" name="zen_mode_alarm" /> diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java index 3ffb2fa7eaad..22367605d0a1 100644 --- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE; import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; @@ -34,6 +36,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.StringRes; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.RefreshCallbackItem; import android.app.servertransaction.ResumeActivityItem; @@ -44,10 +47,13 @@ import android.os.Handler; import android.os.RemoteException; import android.util.ArrayMap; import android.util.ArraySet; +import android.widget.Toast; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; +import com.android.server.UiThread; import java.util.Map; import java.util.Set; @@ -232,6 +238,27 @@ final class DisplayRotationCompatPolicy { activity.mLetterboxUiController.setIsRefreshAfterRotationRequested(false); } + /** + * Notifies that animation in {@link ScreenAnimationRotation} has finished. + * + * <p>This class uses this signal as a trigger for notifying the user about forced rotation + * reason with the {@link Toast}. + */ + void onScreenRotationAnimationFinished() { + if (!isTreatmentEnabledForDisplay() || mCameraIdPackageBiMap.isEmpty()) { + return; + } + ActivityRecord topActivity = mDisplayContent.topRunningActivity( + /* considerKeyguardState= */ true); + if (topActivity == null + // Checking windowing mode on activity level because we don't want to + // show toast in case of activity embedding. + || topActivity.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) { + return; + } + showToast(R.string.display_rotation_camera_compat_toast_after_rotation); + } + String getSummaryForDisplayRotationHistoryRecord() { String summaryIfEnabled = ""; if (isTreatmentEnabledForDisplay()) { @@ -334,7 +361,28 @@ final class DisplayRotationCompatPolicy { } mCameraIdPackageBiMap.put(packageName, cameraId); } - updateOrientationWithWmLock(); + ActivityRecord topActivity = mDisplayContent.topRunningActivity( + /* considerKeyguardState= */ true); + if (topActivity == null || topActivity.getTask() == null) { + return; + } + // Checking whether an activity in fullscreen rather than the task as this camera compat + // treatment doesn't cover activity embedding. + if (topActivity.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { + updateOrientationWithWmLock(); + return; + } + // Checking that the whole app is in multi-window mode as we shouldn't show toast + // for the activity embedding case. + if (topActivity.getTask().getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) { + showToast(R.string.display_rotation_camera_compat_toast_in_split_screen); + } + } + + @VisibleForTesting + void showToast(@StringRes int stringRes) { + UiThread.getHandler().post( + () -> Toast.makeText(mWmService.mContext, stringRes, Toast.LENGTH_LONG).show()); } private synchronized void notifyCameraClosed(@NonNull String cameraId) { @@ -396,6 +444,10 @@ final class DisplayRotationCompatPolicy { private final Map<String, String> mPackageToCameraIdMap = new ArrayMap<>(); private final Map<String, String> mCameraIdToPackageMap = new ArrayMap<>(); + boolean isEmpty() { + return mCameraIdToPackageMap.isEmpty(); + } + void put(String packageName, String cameraId) { // Always using the last connected camera ID for the package even for the concurrent // camera use case since we can't guess which camera is more important anyway. diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index fd8b614de9b7..ef45c221a6cc 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -800,6 +800,10 @@ class ScreenRotationAnimation { if (mDisplayContent.getRotationAnimation() == ScreenRotationAnimation.this) { // It also invokes kill(). mDisplayContent.setRotationAnimation(null); + if (mDisplayContent.mDisplayRotationCompatPolicy != null) { + mDisplayContent.mDisplayRotationCompatPolicy + .onScreenRotationAnimationFinished(); + } } else { kill(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java index 45b30b204801..4954e89c1ffd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE; import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; @@ -30,7 +31,9 @@ import static android.view.Surface.ROTATION_90; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static org.junit.Assert.assertEquals; @@ -58,6 +61,8 @@ import android.view.Surface.Rotation; import androidx.test.filters.SmallTest; +import com.android.internal.R; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -128,6 +133,69 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { } @Test + public void testOpenedCameraInSplitScreen_showToast() { + configureActivity(SCREEN_ORIENTATION_PORTRAIT); + spyOn(mTask); + spyOn(mDisplayRotationCompatPolicy); + doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mActivity).getWindowingMode(); + doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mTask).getWindowingMode(); + + mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); + + verify(mDisplayRotationCompatPolicy).showToast( + R.string.display_rotation_camera_compat_toast_in_split_screen); + } + + @Test + public void testOnScreenRotationAnimationFinished_treatmentNotEnabled_doNotShowToast() { + when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled( + /* checkDeviceConfig */ anyBoolean())) + .thenReturn(false); + spyOn(mDisplayRotationCompatPolicy); + + mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished(); + + verify(mDisplayRotationCompatPolicy, never()).showToast( + R.string.display_rotation_camera_compat_toast_after_rotation); + } + + @Test + public void testOnScreenRotationAnimationFinished_noOpenCamera_doNotShowToast() { + spyOn(mDisplayRotationCompatPolicy); + + mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished(); + + verify(mDisplayRotationCompatPolicy, never()).showToast( + R.string.display_rotation_camera_compat_toast_after_rotation); + } + + @Test + public void testOnScreenRotationAnimationFinished_notFullscreen_doNotShowToast() { + configureActivity(SCREEN_ORIENTATION_PORTRAIT); + doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mActivity).getWindowingMode(); + spyOn(mDisplayRotationCompatPolicy); + + mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); + + mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished(); + + verify(mDisplayRotationCompatPolicy, never()).showToast( + R.string.display_rotation_camera_compat_toast_after_rotation); + } + + @Test + public void testOnScreenRotationAnimationFinished_showToast() { + configureActivity(SCREEN_ORIENTATION_PORTRAIT); + mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); + spyOn(mDisplayRotationCompatPolicy); + + mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished(); + + verify(mDisplayRotationCompatPolicy).showToast( + R.string.display_rotation_camera_compat_toast_after_rotation); + } + + @Test public void testTreatmentNotEnabled_noForceRotationOrRefresh() throws Exception { when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled( /* checkDeviceConfig */ anyBoolean())) |