summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/test-current.txt4
-rw-r--r--core/java/android/app/KeyguardManager.java11
-rw-r--r--core/java/android/app/UiAutomationConnection.java12
-rw-r--r--core/java/android/app/WallpaperManager.java84
-rw-r--r--core/java/android/app/admin/WifiSsidPolicy.java7
-rw-r--r--core/java/android/hardware/input/KeyboardLayout.java23
-rw-r--r--core/java/android/service/notification/NotificationRankingUpdate.java131
-rw-r--r--core/java/android/window/ScreenCapture.java69
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java4
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java44
-rw-r--r--core/jni/android_window_ScreenCapture.cpp22
-rw-r--r--core/res/res/values-af/strings.xml3
-rw-r--r--core/res/res/values-am/strings.xml3
-rw-r--r--core/res/res/values-ar/strings.xml3
-rw-r--r--core/res/res/values-as/strings.xml3
-rw-r--r--core/res/res/values-az/strings.xml3
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml7
-rw-r--r--core/res/res/values-be/strings.xml7
-rw-r--r--core/res/res/values-bg/strings.xml3
-rw-r--r--core/res/res/values-bn/strings.xml3
-rw-r--r--core/res/res/values-bs/strings.xml5
-rw-r--r--core/res/res/values-ca/strings.xml3
-rw-r--r--core/res/res/values-cs/strings.xml3
-rw-r--r--core/res/res/values-da/strings.xml3
-rw-r--r--core/res/res/values-de/strings.xml3
-rw-r--r--core/res/res/values-el/strings.xml5
-rw-r--r--core/res/res/values-en-rAU/strings.xml3
-rw-r--r--core/res/res/values-en-rCA/strings.xml3
-rw-r--r--core/res/res/values-en-rGB/strings.xml3
-rw-r--r--core/res/res/values-en-rIN/strings.xml3
-rw-r--r--core/res/res/values-en-rXC/strings.xml3
-rw-r--r--core/res/res/values-es-rUS/strings.xml5
-rw-r--r--core/res/res/values-es/strings.xml5
-rw-r--r--core/res/res/values-et/strings.xml3
-rw-r--r--core/res/res/values-eu/strings.xml3
-rw-r--r--core/res/res/values-fa/strings.xml3
-rw-r--r--core/res/res/values-fi/strings.xml3
-rw-r--r--core/res/res/values-fr-rCA/strings.xml3
-rw-r--r--core/res/res/values-fr/strings.xml3
-rw-r--r--core/res/res/values-gl/strings.xml3
-rw-r--r--core/res/res/values-gu/strings.xml3
-rw-r--r--core/res/res/values-hi/strings.xml5
-rw-r--r--core/res/res/values-hr/strings.xml5
-rw-r--r--core/res/res/values-hu/strings.xml3
-rw-r--r--core/res/res/values-hy/strings.xml3
-rw-r--r--core/res/res/values-in/strings.xml3
-rw-r--r--core/res/res/values-is/strings.xml3
-rw-r--r--core/res/res/values-it/strings.xml3
-rw-r--r--core/res/res/values-iw/strings.xml3
-rw-r--r--core/res/res/values-ja/strings.xml7
-rw-r--r--core/res/res/values-ka/strings.xml3
-rw-r--r--core/res/res/values-kk/strings.xml7
-rw-r--r--core/res/res/values-km/strings.xml5
-rw-r--r--core/res/res/values-kn/strings.xml7
-rw-r--r--core/res/res/values-ko/strings.xml5
-rw-r--r--core/res/res/values-ky/strings.xml3
-rw-r--r--core/res/res/values-lo/strings.xml3
-rw-r--r--core/res/res/values-lt/strings.xml3
-rw-r--r--core/res/res/values-lv/strings.xml3
-rw-r--r--core/res/res/values-mk/strings.xml3
-rw-r--r--core/res/res/values-ml/strings.xml3
-rw-r--r--core/res/res/values-mn/strings.xml3
-rw-r--r--core/res/res/values-mr/strings.xml3
-rw-r--r--core/res/res/values-ms/strings.xml3
-rw-r--r--core/res/res/values-my/strings.xml5
-rw-r--r--core/res/res/values-nb/strings.xml3
-rw-r--r--core/res/res/values-ne/strings.xml61
-rw-r--r--core/res/res/values-nl/strings.xml3
-rw-r--r--core/res/res/values-or/strings.xml3
-rw-r--r--core/res/res/values-pa/strings.xml7
-rw-r--r--core/res/res/values-pl/strings.xml3
-rw-r--r--core/res/res/values-pt-rBR/strings.xml5
-rw-r--r--core/res/res/values-pt-rPT/strings.xml5
-rw-r--r--core/res/res/values-pt/strings.xml5
-rw-r--r--core/res/res/values-ro/strings.xml3
-rw-r--r--core/res/res/values-ru/strings.xml7
-rw-r--r--core/res/res/values-si/strings.xml3
-rw-r--r--core/res/res/values-sk/strings.xml3
-rw-r--r--core/res/res/values-sl/strings.xml3
-rw-r--r--core/res/res/values-sq/strings.xml3
-rw-r--r--core/res/res/values-sr/strings.xml7
-rw-r--r--core/res/res/values-sv/strings.xml3
-rw-r--r--core/res/res/values-sw/strings.xml3
-rw-r--r--core/res/res/values-ta/strings.xml3
-rw-r--r--core/res/res/values-te/strings.xml9
-rw-r--r--core/res/res/values-th/strings.xml5
-rw-r--r--core/res/res/values-tl/strings.xml3
-rw-r--r--core/res/res/values-tr/strings.xml3
-rw-r--r--core/res/res/values-uk/strings.xml3
-rw-r--r--core/res/res/values-ur/strings.xml3
-rw-r--r--core/res/res/values-uz/strings.xml3
-rw-r--r--core/res/res/values-vi/strings.xml3
-rw-r--r--core/res/res/values-zh-rCN/strings.xml3
-rw-r--r--core/res/res/values-zh-rHK/strings.xml3
-rw-r--r--core/res/res/values-zh-rTW/strings.xml3
-rw-r--r--core/res/res/values-zu/strings.xml3
-rw-r--r--core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java195
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java63
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java209
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp27
-rw-r--r--libs/hwui/renderthread/CanvasContext.h6
-rw-r--r--media/java/android/media/MediaCodec.java8
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-or/strings.xml2
-rw-r--r--packages/SettingsLib/res/layout/dialog_with_icon.xml1
-rw-r--r--packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml92
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java7
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java41
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/utils/CustomDialogHelper.java8
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java6
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java35
-rw-r--r--packages/Shell/AndroidManifest.xml2
-rw-r--r--packages/Shell/src/com/android/shell/Screenshooter.java10
-rw-r--r--packages/SystemUI/res-keyguard/values-ne/strings.xml6
-rw-r--r--packages/SystemUI/res/layout/media_output_list_item_advanced.xml4
-rw-r--r--packages/SystemUI/src/com/android/systemui/SwipeHelper.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java150
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperService.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt181
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt78
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterModule.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java56
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java58
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java44
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperServiceTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt97
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt297
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java74
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java49
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java93
-rw-r--r--services/core/java/com/android/server/VcnManagementService.java6
-rw-r--r--services/core/java/com/android/server/VpnManagerService.java3
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java17
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueueModernImpl.java6
-rw-r--r--services/core/java/com/android/server/am/StackTracesDumpHelper.java2
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java16
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java4
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java1
-rw-r--r--services/core/java/com/android/server/audio/BtHelper.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java12
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java12
-rw-r--r--services/core/java/com/android/server/dreams/DreamManagerService.java11
-rw-r--r--services/core/java/com/android/server/input/KeyboardLayoutManager.java181
-rw-r--r--services/core/java/com/android/server/input/KeyboardMetricsCollector.java278
-rw-r--r--services/core/java/com/android/server/input/PersistentDataStore.java4
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java22
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsStorage.java50
-rw-r--r--services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java51
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java50
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java7
-rw-r--r--services/core/java/com/android/server/wm/LetterboxConfiguration.java3
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java12
-rw-r--r--services/core/java/com/android/server/wm/SplashScreenExceptionList.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java32
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java36
-rw-r--r--services/tests/servicestests/src/com/android/server/input/KeyboardMetricsCollectorTests.kt179
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java25
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java129
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java11
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java10
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java66
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java22
-rw-r--r--tests/vcn/java/com/android/server/VcnManagementServiceTest.java8
213 files changed, 3041 insertions, 1530 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 9bd29700a12d..9f90425d0591 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2958,6 +2958,10 @@ package android.service.notification {
method @Deprecated public boolean isBound();
}
+ public class NotificationRankingUpdate implements android.os.Parcelable {
+ method public final boolean isFdNotNullAndClosed();
+ }
+
}
package android.service.quickaccesswallet {
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 09450f59ed3d..b71cb4a388ab 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -117,6 +117,17 @@ public class KeyguardManager {
"android.app.action.CONFIRM_REMOTE_DEVICE_CREDENTIAL";
/**
+ * Intent used to prompt user for device credential for entering repair
+ * mode. If the credential is verified successfully, then the information
+ * needed to verify the credential again will be written to a location that
+ * is available to repair mode. This makes it possible for repair mode to
+ * require that the same credential be provided to exit repair mode.
+ * @hide
+ */
+ public static final String ACTION_PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL =
+ "android.app.action.PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL";
+
+ /**
* A CharSequence dialog title to show to the user when used with a
* {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
* @hide
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index d96a9d104ec2..34f0964cf823 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -39,7 +39,6 @@ import android.os.ServiceManager;
import android.os.UserHandle;
import android.permission.IPermissionManager;
import android.util.Log;
-import android.util.Pair;
import android.view.IWindowManager;
import android.view.InputDevice;
import android.view.InputEvent;
@@ -52,7 +51,8 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.IAccessibilityManager;
import android.window.ScreenCapture;
import android.window.ScreenCapture.CaptureArgs;
-import android.window.ScreenCapture.ScreenCaptureListener;
+import android.window.ScreenCapture.ScreenshotHardwareBuffer;
+import android.window.ScreenCapture.SynchronousScreenCaptureListener;
import libcore.io.IoUtils;
@@ -235,12 +235,12 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
final CaptureArgs captureArgs = new CaptureArgs.Builder<>()
.setSourceCrop(crop)
.build();
- Pair<ScreenCaptureListener, ScreenCapture.ScreenshotSync> syncScreenCapture =
+ SynchronousScreenCaptureListener syncScreenCapture =
ScreenCapture.createSyncCaptureListener();
mWindowManager.captureDisplay(DEFAULT_DISPLAY, captureArgs,
- syncScreenCapture.first);
- final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
- syncScreenCapture.second.get();
+ syncScreenCapture);
+ final ScreenshotHardwareBuffer screenshotBuffer =
+ syncScreenCapture.getBuffer();
return screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
} catch (RemoteException re) {
re.rethrowAsRuntimeException();
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index de5bcc796f2a..1c5332a3c8bd 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -913,7 +913,7 @@ public class WallpaperManager {
* instead the default system wallpaper is returned
* (some versions of T may throw a {@code SecurityException}).</li>
* <li>From version U, this method should not be used
- * and will always throw a @code SecurityException}.</li>
+ * and will always throw a {@code SecurityException}.</li>
* <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
* can still access the real wallpaper on all versions. </li>
* </ul>
@@ -935,18 +935,9 @@ public class WallpaperManager {
}
/**
- * <strong> Important note: </strong>
- * <ul>
- * <li>Up to version S, this method requires the
- * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
- * <li>Starting in T, directly accessing the wallpaper is not possible anymore,
- * instead the default system wallpaper is returned
- * (some versions of T may throw a {@code SecurityException}).</li>
- * <li>From version U, this method should not be used
- * and will always throw a @code SecurityException}.</li>
- * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
- * can still access the real wallpaper on all versions. </li>
- * </ul>
+ * <strong> Important note: </strong> only apps with
+ * {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} should use this method.
+ * Otherwise, a {@code SecurityException} will be thrown.
*
* <p>
* Retrieve the requested wallpaper for the specified wallpaper type if the wallpaper is not
@@ -1206,7 +1197,7 @@ public class WallpaperManager {
* instead the default system wallpaper is returned
* (some versions of T may throw a {@code SecurityException}).</li>
* <li>From version U, this method should not be used
- * and will always throw a @code SecurityException}.</li>
+ * and will always throw a {@code SecurityException}.</li>
* <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
* can still access the real wallpaper on all versions. </li>
* </ul>
@@ -1228,18 +1219,9 @@ public class WallpaperManager {
}
/**
- * <strong> Important note: </strong>
- * <ul>
- * <li>Up to version S, this method requires the
- * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
- * <li>Starting in T, directly accessing the wallpaper is not possible anymore,
- * instead the default system wallpaper is returned
- * (some versions of T may throw a {@code SecurityException}).</li>
- * <li>From version U, this method should not be used
- * and will always throw a @code SecurityException}.</li>
- * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
- * can still access the real wallpaper on all versions. </li>
- * </ul>
+ * <strong> Important note: </strong> only apps with
+ * {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} should use this method.
+ * Otherwise, a {@code SecurityException} will be thrown.
*
* <p>
* Equivalent to {@link #getDrawable(int)}.
@@ -1268,7 +1250,7 @@ public class WallpaperManager {
* instead the default wallpaper is returned
* (some versions of T may throw a {@code SecurityException}).</li>
* <li>From version U, this method should not be used
- * and will always throw a @code SecurityException}.</li>
+ * and will always throw a {@code SecurityException}.</li>
* <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
* can still access the real wallpaper on all versions. </li>
* </ul>
@@ -1290,19 +1272,9 @@ public class WallpaperManager {
}
/**
- * <strong> Important note: </strong>
- * <ul>
- * <li>Up to version S, this method requires the
- * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
- * <li>Starting in T, directly accessing the wallpaper is not possible anymore,
- * instead the default system wallpaper is returned
- * (some versions of T may throw a {@code SecurityException}).</li>
- * <li>From version U, this method should not be used
- * and will always throw a @code SecurityException}.</li>
- * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
- * can still access the real wallpaper on all versions. </li>
- * </ul>
- * <br>
+ * <strong> Important note: </strong> only apps with
+ * {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} should use this method.
+ * Otherwise, a {@code SecurityException} will be thrown.
*
* Like {@link #getDrawable(int)}, but the returned Drawable has a number
* of limitations to reduce its overhead as much as possible. It will
@@ -1334,18 +1306,9 @@ public class WallpaperManager {
}
/**
- * <strong> Important note: </strong>
- * <ul>
- * <li>Up to version S, this method requires the
- * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
- * <li>Starting in T, directly accessing the wallpaper is not possible anymore,
- * instead the default system wallpaper is returned
- * (some versions of T may throw a {@code SecurityException}).</li>
- * <li>From version U, this method should not be used
- * and will always throw a @code SecurityException}.</li>
- * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
- * can still access the real wallpaper on all versions. </li>
- * </ul>
+ * <strong> Important note: </strong> only apps with
+ * {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} should use this method.
+ * Otherwise, a {@code SecurityException} will be thrown.
*
* <p>
* Equivalent to {@link #getFastDrawable()}.
@@ -1364,18 +1327,9 @@ public class WallpaperManager {
}
/**
- * <strong> Important note: </strong>
- * <ul>
- * <li>Up to version S, this method requires the
- * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
- * <li>Starting in T, directly accessing the wallpaper is not possible anymore,
- * instead the default system wallpaper is returned
- * (some versions of T may throw a {@code SecurityException}).</li>
- * <li>From version U, this method should not be used
- * and will always throw a @code SecurityException}.</li>
- * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
- * can still access the real wallpaper on all versions. </li>
- * </ul>
+ * <strong> Important note: </strong> only apps with
+ * {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
+ * should use this method. Otherwise, a {@code SecurityException} will be thrown.
*
* <p>
* Equivalent to {@link #getFastDrawable(int)}.
@@ -1562,7 +1516,7 @@ public class WallpaperManager {
* instead the default system wallpaper is returned
* (some versions of T may throw a {@code SecurityException}).</li>
* <li>From version U, this method should not be used
- * and will always throw a @code SecurityException}.</li>
+ * and will always throw a {@code SecurityException}.</li>
* <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
* can still access the real wallpaper on all versions. </li>
* </ul>
diff --git a/core/java/android/app/admin/WifiSsidPolicy.java b/core/java/android/app/admin/WifiSsidPolicy.java
index 3fefe4bda331..ed53967b86e4 100644
--- a/core/java/android/app/admin/WifiSsidPolicy.java
+++ b/core/java/android/app/admin/WifiSsidPolicy.java
@@ -135,6 +135,10 @@ public final class WifiSsidPolicy implements Parcelable {
dest.writeArraySet(mSsids);
}
+ /**
+ * Two instances of WifiSsidPolicy is considered equal if they have
+ * the same WifiSsidPolicyType and the same set of WifiSsids
+ */
@Override
public boolean equals(Object thatObject) {
if (this == thatObject) {
@@ -147,6 +151,9 @@ public final class WifiSsidPolicy implements Parcelable {
return mPolicyType == that.mPolicyType && Objects.equals(mSsids, that.mSsids);
}
+ /**
+ * Returns the hash code value of WifiSsidPolicyType and WifiSsid set
+ */
@Override
public int hashCode() {
return Objects.hash(mPolicyType, mSsids);
diff --git a/core/java/android/hardware/input/KeyboardLayout.java b/core/java/android/hardware/input/KeyboardLayout.java
index 0311da4b645f..4403251e0488 100644
--- a/core/java/android/hardware/input/KeyboardLayout.java
+++ b/core/java/android/hardware/input/KeyboardLayout.java
@@ -73,7 +73,7 @@ public final class KeyboardLayout implements Parcelable, Comparable<KeyboardLayo
private final int mProductId;
/** Currently supported Layout types in the KCM files */
- private enum LayoutType {
+ public enum LayoutType {
UNDEFINED(0, LAYOUT_TYPE_UNDEFINED),
QWERTY(1, LAYOUT_TYPE_QWERTY),
QWERTZ(2, LAYOUT_TYPE_QWERTZ),
@@ -88,9 +88,11 @@ public final class KeyboardLayout implements Parcelable, Comparable<KeyboardLayo
private final int mValue;
private final String mName;
private static final Map<Integer, LayoutType> VALUE_TO_ENUM_MAP = new HashMap<>();
+ private static final Map<String, LayoutType> NAME_TO_ENUM_MAP = new HashMap<>();
static {
for (LayoutType type : LayoutType.values()) {
VALUE_TO_ENUM_MAP.put(type.mValue, type);
+ NAME_TO_ENUM_MAP.put(type.mName, type);
}
}
@@ -110,6 +112,25 @@ public final class KeyboardLayout implements Parcelable, Comparable<KeyboardLayo
private String getName() {
return mName;
}
+
+ /**
+ * Returns enum value for provided layout type
+ * @param layoutName name of the layout type
+ * @return int value corresponding to the LayoutType enum that matches the layout name.
+ * (LayoutType.UNDEFINED if no match found)
+ */
+ public static int getLayoutTypeEnumValue(String layoutName) {
+ return NAME_TO_ENUM_MAP.getOrDefault(layoutName, UNDEFINED).getValue();
+ }
+
+ /**
+ * Returns name for provided layout type enum value
+ * @param enumValue value representation for LayoutType enum
+ * @return Layout name corresponding to the enum value (LAYOUT_TYPE_UNDEFINED if not found)
+ */
+ public static String getLayoutNameFromValue(int enumValue) {
+ return VALUE_TO_ENUM_MAP.getOrDefault(enumValue, UNDEFINED).getName();
+ }
}
@NonNull
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index a853714c0e9d..75640bd47ab9 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -16,32 +16,117 @@
package android.service.notification;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.SharedMemory;
+import android.system.ErrnoException;
+import android.system.OsConstants;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
+
+import java.nio.ByteBuffer;
/**
+ * Represents an update to notification rankings.
* @hide
*/
+@SuppressLint({"ParcelNotFinal", "ParcelCreator"})
+@TestApi
public class NotificationRankingUpdate implements Parcelable {
private final NotificationListenerService.RankingMap mRankingMap;
+ // The ranking map is stored in shared memory when parceled, for sending across the binder.
+ // This is done because the ranking map can grow large if there are many notifications.
+ private SharedMemory mRankingMapFd = null;
+ private final String mSharedMemoryName = "NotificationRankingUpdatedSharedMemory";
+
+ /**
+ * @hide
+ */
public NotificationRankingUpdate(NotificationListenerService.Ranking[] rankings) {
mRankingMap = new NotificationListenerService.RankingMap(rankings);
}
+ /**
+ * @hide
+ */
public NotificationRankingUpdate(Parcel in) {
- mRankingMap = in.readParcelable(getClass().getClassLoader(), android.service.notification.NotificationListenerService.RankingMap.class);
+ if (SystemUiSystemPropertiesFlags.getResolver().isEnabled(
+ SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) {
+ // Recover the ranking map from the SharedMemory and store it in mapParcel.
+ final Parcel mapParcel = Parcel.obtain();
+ ByteBuffer buffer = null;
+ try {
+ // The ranking map should be stored in shared memory when it is parceled, so we
+ // unwrap the SharedMemory object.
+ mRankingMapFd = in.readParcelable(getClass().getClassLoader(), SharedMemory.class);
+
+ // In the case that the ranking map can't be read, readParcelable may return null.
+ // In this case, we set mRankingMap to null;
+ if (mRankingMapFd == null) {
+ mRankingMap = null;
+ return;
+ }
+ // We only need read-only access to the shared memory region.
+ buffer = mRankingMapFd.mapReadOnly();
+ if (buffer == null) {
+ mRankingMap = null;
+ return;
+ }
+ byte[] payload = new byte[buffer.remaining()];
+ buffer.get(payload);
+ mapParcel.unmarshall(payload, 0, payload.length);
+ mapParcel.setDataPosition(0);
+
+ mRankingMap = mapParcel.readParcelable(getClass().getClassLoader(),
+ android.service.notification.NotificationListenerService.RankingMap.class);
+ } catch (ErrnoException e) {
+ // TODO(b/284297289): remove throw when associated flag is moved to droidfood, to
+ // avoid crashes; change to Log.wtf.
+ throw new RuntimeException(e);
+ } finally {
+ mapParcel.recycle();
+ if (buffer != null) {
+ mRankingMapFd.unmap(buffer);
+ }
+ }
+ } else {
+ mRankingMap = in.readParcelable(getClass().getClassLoader(),
+ android.service.notification.NotificationListenerService.RankingMap.class);
+ }
}
+ /**
+ * Confirms that the SharedMemory file descriptor is closed. Should only be used for testing.
+ * @hide
+ */
+ @TestApi
+ public final boolean isFdNotNullAndClosed() {
+ return mRankingMapFd != null && mRankingMapFd.getFd() == -1;
+ }
+
+ /**
+ * @hide
+ */
public NotificationListenerService.RankingMap getRankingMap() {
return mRankingMap;
}
+ /**
+ * @hide
+ */
@Override
public int describeContents() {
return 0;
}
+ /**
+ * @hide
+ */
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
@@ -51,11 +136,51 @@ public class NotificationRankingUpdate implements Parcelable {
return mRankingMap.equals(other.mRankingMap);
}
+ /**
+ * @hide
+ */
@Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeParcelable(mRankingMap, flags);
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ if (SystemUiSystemPropertiesFlags.getResolver().isEnabled(
+ SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) {
+ final Parcel mapParcel = Parcel.obtain();
+ try {
+ // Parcels the ranking map and measures its size.
+ mapParcel.writeParcelable(mRankingMap, flags);
+ int mapSize = mapParcel.dataSize();
+
+ // Creates a new SharedMemory object with enough space to hold the ranking map.
+ SharedMemory mRankingMapFd = SharedMemory.create(mSharedMemoryName, mapSize);
+ if (mRankingMapFd == null) {
+ return;
+ }
+
+ // Gets a read/write buffer mapping the entire shared memory region.
+ final ByteBuffer buffer = mRankingMapFd.mapReadWrite();
+
+ // Puts the ranking map into the shared memory region buffer.
+ buffer.put(mapParcel.marshall(), 0, mapSize);
+
+ // Protects the region from being written to, by setting it to be read-only.
+ mRankingMapFd.setProtect(OsConstants.PROT_READ);
+
+ // Puts the SharedMemory object in the parcel.
+ out.writeParcelable(mRankingMapFd, flags);
+ } catch (ErrnoException e) {
+ // TODO(b/284297289): remove throw when associated flag is moved to droidfood, to
+ // avoid crashes; change to Log.wtf.
+ throw new RuntimeException(e);
+ } finally {
+ mapParcel.recycle();
+ }
+ } else {
+ out.writeParcelable(mRankingMap, flags);
+ }
}
+ /**
+ * @hide
+ */
public static final @android.annotation.NonNull Parcelable.Creator<NotificationRankingUpdate> CREATOR
= new Parcelable.Creator<NotificationRankingUpdate>() {
public NotificationRankingUpdate createFromParcel(Parcel parcel) {
diff --git a/core/java/android/window/ScreenCapture.java b/core/java/android/window/ScreenCapture.java
index fa7f577dadb8..0cc9c644f8ea 100644
--- a/core/java/android/window/ScreenCapture.java
+++ b/core/java/android/window/ScreenCapture.java
@@ -27,7 +27,6 @@ import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
-import android.util.Pair;
import android.view.SurfaceControl;
import libcore.util.NativeAllocationRegistry;
@@ -73,14 +72,14 @@ public class ScreenCapture {
*/
public static ScreenshotHardwareBuffer captureDisplay(
DisplayCaptureArgs captureArgs) {
- Pair<ScreenCaptureListener, ScreenshotSync> syncScreenCapture = createSyncCaptureListener();
- int status = captureDisplay(captureArgs, syncScreenCapture.first);
+ SynchronousScreenCaptureListener syncScreenCapture = createSyncCaptureListener();
+ int status = captureDisplay(captureArgs, syncScreenCapture);
if (status != 0) {
return null;
}
try {
- return syncScreenCapture.second.get();
+ return syncScreenCapture.getBuffer();
} catch (Exception e) {
return null;
}
@@ -133,14 +132,14 @@ public class ScreenCapture {
* @hide
*/
public static ScreenshotHardwareBuffer captureLayers(LayerCaptureArgs captureArgs) {
- Pair<ScreenCaptureListener, ScreenshotSync> syncScreenCapture = createSyncCaptureListener();
- int status = captureLayers(captureArgs, syncScreenCapture.first);
+ SynchronousScreenCaptureListener syncScreenCapture = createSyncCaptureListener();
+ int status = captureLayers(captureArgs, syncScreenCapture);
if (status != 0) {
return null;
}
try {
- return syncScreenCapture.second.get();
+ return syncScreenCapture.getBuffer();
} catch (Exception e) {
return null;
}
@@ -743,14 +742,35 @@ public class ScreenCapture {
* A helper method to handle the async screencapture callbacks synchronously. This should only
* be used if the screencapture caller doesn't care that it blocks waiting for a screenshot.
*
- * @return a Pair that holds the {@link ScreenCaptureListener} that should be used for capture
- * calls into SurfaceFlinger and a {@link ScreenshotSync} object to retrieve the results.
+ * @return a {@link SynchronousScreenCaptureListener} that should be used for capture
+ * calls into SurfaceFlinger.
*/
- public static Pair<ScreenCaptureListener, ScreenshotSync> createSyncCaptureListener() {
- final ScreenshotSync screenshotSync = new ScreenshotSync();
- final ScreenCaptureListener screenCaptureListener = new ScreenCaptureListener(
- screenshotSync::setScreenshotHardwareBuffer);
- return new Pair<>(screenCaptureListener, screenshotSync);
+ public static SynchronousScreenCaptureListener createSyncCaptureListener() {
+ ScreenshotHardwareBuffer[] bufferRef = new ScreenshotHardwareBuffer[1];
+ CountDownLatch latch = new CountDownLatch(1);
+ Consumer<ScreenshotHardwareBuffer> consumer = buffer -> {
+ bufferRef[0] = buffer;
+ latch.countDown();
+ };
+
+ return new SynchronousScreenCaptureListener(consumer) {
+ // In order to avoid requiring two GC cycles to clean up the consumer and the buffer
+ // it references, the underlying JNI listener holds a weak reference to the consumer.
+ // This property exists to ensure the consumer stays alive during the listener's
+ // lifetime.
+ private Consumer<ScreenshotHardwareBuffer> mConsumer = consumer;
+
+ @Override
+ public ScreenshotHardwareBuffer getBuffer() {
+ try {
+ latch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
+ return bufferRef[0];
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to wait for screen capture result", e);
+ return null;
+ }
+ }
+ };
}
/**
@@ -758,28 +778,15 @@ public class ScreenCapture {
* {@link #captureLayers(LayerCaptureArgs, ScreenCaptureListener)} or
* {@link #captureDisplay(DisplayCaptureArgs, ScreenCaptureListener)}
*/
- public static class ScreenshotSync {
- private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
- private ScreenshotHardwareBuffer mScreenshotHardwareBuffer;
-
- private void setScreenshotHardwareBuffer(
- ScreenshotHardwareBuffer screenshotHardwareBuffer) {
- mScreenshotHardwareBuffer = screenshotHardwareBuffer;
- mCountDownLatch.countDown();
+ public abstract static class SynchronousScreenCaptureListener extends ScreenCaptureListener {
+ SynchronousScreenCaptureListener(Consumer<ScreenshotHardwareBuffer> consumer) {
+ super(consumer);
}
/**
* Get the {@link ScreenshotHardwareBuffer} synchronously. This can be null if the
* screenshot failed or if there was no callback in {@link #SCREENSHOT_WAIT_TIME_S} seconds.
*/
- public ScreenshotHardwareBuffer get() {
- try {
- mCountDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
- return mScreenshotHardwareBuffer;
- } catch (Exception e) {
- Log.e(TAG, "Failed to wait for screen capture result", e);
- return null;
- }
- }
+ public abstract ScreenshotHardwareBuffer getBuffer();
}
}
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index c9e76009136a..6b8ae946e68c 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -85,6 +85,10 @@ public class SystemUiSystemPropertiesFlags {
/** Gating the holding of WakeLocks until NLSes are told about a new notification. */
public static final Flag WAKE_LOCK_FOR_POSTING_NOTIFICATION =
devFlag("persist.sysui.notification.wake_lock_for_posting_notification");
+
+ /** Gating storing NotificationRankingUpdate ranking map in shared memory. */
+ public static final Flag RANKING_UPDATE_ASHMEM = devFlag(
+ "persist.sysui.notification.ranking_update_ashmem");
}
//// == End of flags. Everything below this line is the implementation. == ////
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 92cfa670045e..66b69c53a684 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -47,6 +47,7 @@ import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
@@ -160,9 +161,17 @@ public class LockPatternUtils {
*/
public static final int VERIFY_FLAG_REQUEST_GK_PW_HANDLE = 1 << 0;
+ /**
+ * Flag provided to {@link #verifyCredential(LockscreenCredential, int, int)} . If set, the
+ * method writes the password data to the repair mode file after the credential is verified
+ * successfully.
+ */
+ public static final int VERIFY_FLAG_WRITE_REPAIR_MODE_PW = 1 << 1;
+
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, value = {
- VERIFY_FLAG_REQUEST_GK_PW_HANDLE
+ VERIFY_FLAG_REQUEST_GK_PW_HANDLE,
+ VERIFY_FLAG_WRITE_REPAIR_MODE_PW
})
public @interface VerifyFlag {}
@@ -201,6 +210,8 @@ public class LockPatternUtils {
public static final String CURRENT_LSKF_BASED_PROTECTOR_ID_KEY = "sp-handle";
public static final String PASSWORD_HISTORY_DELIMITER = ",";
+ private static final String GSI_RUNNING_PROP = "ro.gsid.image_running";
+
/**
* drives the pin auto confirmation feature availability in code logic.
*/
@@ -1847,6 +1858,37 @@ public class LockPatternUtils {
}
/**
+ * Return {@code true} if repair mode is supported by the device.
+ */
+ public static boolean isRepairModeSupported(Context context) {
+ return context.getResources().getBoolean(
+ com.android.internal.R.bool.config_repairModeSupported);
+ }
+
+ /**
+ * Return {@code true} if repair mode is active on the device.
+ */
+ public static boolean isRepairModeActive(Context context) {
+ return Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.REPAIR_MODE_ACTIVE, /* def= */ 0) > 0;
+ }
+
+ /**
+ * Return {@code true} if repair mode is supported by the device and the user has been granted
+ * admin privileges.
+ */
+ public static boolean canUserEnterRepairMode(Context context, UserInfo info) {
+ return info != null && info.isAdmin() && isRepairModeSupported(context);
+ }
+
+ /**
+ * Return {@code true} if GSI is running on the device.
+ */
+ public static boolean isGsiRunning() {
+ return SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0;
+ }
+
+ /**
* Attempt to rederive the unified work challenge for the specified profile user and unlock the
* user. If successful, this would allow the user to leave quiet mode automatically without
* additional user authentication.
diff --git a/core/jni/android_window_ScreenCapture.cpp b/core/jni/android_window_ScreenCapture.cpp
index 986dbe9a204c..e729750b812e 100644
--- a/core/jni/android_window_ScreenCapture.cpp
+++ b/core/jni/android_window_ScreenCapture.cpp
@@ -81,22 +81,28 @@ class ScreenCaptureListenerWrapper : public gui::BnScreenCaptureListener {
public:
explicit ScreenCaptureListenerWrapper(JNIEnv* env, jobject jobject) {
env->GetJavaVM(&mVm);
- mConsumerObject = env->NewGlobalRef(jobject);
- LOG_ALWAYS_FATAL_IF(!mConsumerObject, "Failed to make global ref");
+ mConsumerWeak = env->NewWeakGlobalRef(jobject);
}
~ScreenCaptureListenerWrapper() {
- if (mConsumerObject) {
- getenv()->DeleteGlobalRef(mConsumerObject);
- mConsumerObject = nullptr;
+ if (mConsumerWeak) {
+ getenv()->DeleteWeakGlobalRef(mConsumerWeak);
+ mConsumerWeak = nullptr;
}
}
binder::Status onScreenCaptureCompleted(
const gui::ScreenCaptureResults& captureResults) override {
JNIEnv* env = getenv();
+
+ ScopedLocalRef<jobject> consumer{env, env->NewLocalRef(mConsumerWeak)};
+ if (consumer == nullptr) {
+ ALOGE("ScreenCaptureListenerWrapper consumer not alive.");
+ return binder::Status::ok();
+ }
+
if (!captureResults.fenceResult.ok() || captureResults.buffer == nullptr) {
- env->CallVoidMethod(mConsumerObject, gConsumerClassInfo.accept, nullptr);
+ env->CallVoidMethod(consumer.get(), gConsumerClassInfo.accept, nullptr);
checkAndClearException(env, "accept");
return binder::Status::ok();
}
@@ -111,7 +117,7 @@ public:
captureResults.capturedSecureLayers,
captureResults.capturedHdrLayers);
checkAndClearException(env, "builder");
- env->CallVoidMethod(mConsumerObject, gConsumerClassInfo.accept, screenshotHardwareBuffer);
+ env->CallVoidMethod(consumer.get(), gConsumerClassInfo.accept, screenshotHardwareBuffer);
checkAndClearException(env, "accept");
env->DeleteLocalRef(jhardwareBuffer);
env->DeleteLocalRef(screenshotHardwareBuffer);
@@ -119,7 +125,7 @@ public:
}
private:
- jobject mConsumerObject;
+ jweak mConsumerWeak;
JavaVM* mVm;
JNIEnv* getenv() {
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index a6b1fdbc588d..a941dc7a7f99 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometriese hardeware is nie beskikbaar nie"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Stawing is gekanselleer"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nie herken nie"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Gesig word nie herken nie"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Stawing is gekanselleer"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Geen PIN, patroon of wagwoord is gestel nie"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Kon nie staaf nie"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Swiep van bo na onder as jy wil uitgaan."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Het dit"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Draai vir ’n beter aansig"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Verlaat gedeelde skerm vir ’n beter aansig"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Maak <xliff:g id="NAME">%s</xliff:g> in volskerm oop vir ’n beter aansig"</string>
<string name="done_label" msgid="7283767013231718521">"Klaar"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Ure se sirkelglyer"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Minute se sirkelglyer"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index f3896a3a8381..f97768ebdd16 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ባዮሜትራዊ ሃርድዌር አይገኝም"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"ማረጋገጥ ተሰርዟል"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"አልታወቀም"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"መልክ አልታወቀም"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"ማረጋገጥ ተሰርዟል"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"ምንም ፒን፣ ሥርዓተ ጥለት ወይም የይለፍ ቃል አልተቀናበረም"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"ማረጋገጥ ላይ ስህተት"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"ለመውጣት፣ ከላይ ወደታች ጠረግ ያድርጉ።"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"ገባኝ"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ለተሻለ ዕይታ ያሽከርክሩ"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"ለተሻለ ዕይታ የተከፈለ ማያ ገጽን ትተው ይውጡ"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"ለተሻለ ዕይታ <xliff:g id="NAME">%s</xliff:g>ን በሙሉ ገጽ ዕይታ ይክፈቱ"</string>
<string name="done_label" msgid="7283767013231718521">"ተከናውኗል"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"የሰዓታት ክብ ተንሸራታች"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"የደቂቃዎች ክብ ተንሸራታች"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 546713586c8a..f3ee40ec3057 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -624,6 +624,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"معدّات المقاييس الحيوية غير متاحة."</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"تم إلغاء المصادقة."</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"لم يتم التعرف عليها."</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"لم يتم التعرّف على الوجه."</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"تم إلغاء المصادقة."</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"لم يتم ضبط رقم تعريف شخصي أو نقش أو كلمة مرور."</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"خطأ في المصادقة"</string>
@@ -1854,7 +1855,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"للخروج، مرر بسرعة من أعلى إلى أسفل."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"حسنًا"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"يمكنك تدوير الجهاز لرؤية شاشة معاينة الكاميرا بشكل أوضح."</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"يمكنك الخروج من وضع \"تقسيم الشاشة\" لرؤية شاشة معاينة الكاميرا بشكل أوضح."</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"عليك فتح \"<xliff:g id="NAME">%s</xliff:g>\" في وضع ملء الشاشة لرؤية شاشة معاينة الكاميرا بشكل أوضح."</string>
<string name="done_label" msgid="7283767013231718521">"تم"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"شريط التمرير الدائري للساعات"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"شريط التمرير الدائري للدقائق"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 123030175923..840c720f8898 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"বায়োমেট্ৰিক হাৰ্ডৱেৰ উপলব্ধ নহয়"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"বিশ্বাসযোগ্যতাৰ প্ৰমাণীকৰণ বাতিল কৰা হৈছে"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"চিনাক্ত কৰিব পৰা নাই"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"মুখাৱয়ব চিনি পোৱা নাই"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"বিশ্বাসযোগ্যতাৰ প্ৰমাণীকৰণ বাতিল কৰা হৈছে"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"কোনো পিন, আৰ্হি বা পাছৱৰ্ড ছেট কৰা নাই"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"আসোঁৱাহৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰি থকা হৈছে"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"বাহিৰ হ\'বলৈ ওপৰৰপৰা তললৈ ছোৱাইপ কৰক।"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"বুজি পালোঁ"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ভালকৈ চাবলৈ ঘূৰাওক"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"ভালকৈ চাবলৈ বিভাজিত স্ক্ৰীনৰ পৰা বাহিৰ হওক"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"ভালকৈ চাবলৈ <xliff:g id="NAME">%s</xliff:g> পূৰ্ণ স্ক্ৰীনত খোলক"</string>
<string name="done_label" msgid="7283767013231718521">"সম্পন্ন কৰা হ’ল"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"ঘড়ীৰ বৃত্তাকাৰ শ্লাইডাৰ"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"মিনিটৰ বৃত্তাকাৰ শ্লাইডাৰ"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 45b67ebabb2a..5a41074a0cf7 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrik proqram əlçatan deyil"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Doğrulama ləğv edildi"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Tanınmır"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Üz tanınmadı"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Doğrulama ləğv edildi"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Pin, nümunə və ya parol ayarlanmayıb"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Doğrulama zamanı xəta baş verdi"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Çıxmaq üçün yuxarıdan aşağı sürüşdürün."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Anladım"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Daha yaxşı görünüş üçün fırladın"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Daha yaxşı görünüş üçün bölünmüş ekrandan çıxın"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Yaxşı görmək üçün <xliff:g id="NAME">%s</xliff:g> tətbiqini tam ekranda açın"</string>
<string name="done_label" msgid="7283767013231718521">"Hazırdır"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Dairəvi saat slayderi"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Dairəvi dəqiqə slayderi"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index a310cfbad129..32836e44cbdf 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -621,16 +621,17 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrijski hardver nije dostupan"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Potvrda identiteta je otkazana"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nije prepoznato"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Lice nije prepoznato"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Potvrda identiteta je otkazana"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Niste podesili ni PIN, ni šablon, ni lozinku"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Greška pri potvrdi identiteta"</string>
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Koristite zaključavanje ekrana"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Upotrebite zaključavanje ekrana da biste nastavili"</string>
- <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Jako pritisnite senzor"</string>
+ <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Čvrsto pritisnite senzor"</string>
<string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Prepoznavanje otiska prsta nije uspelo. Probajte ponovo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Obrišite senzor za otisak prsta i probajte ponovo"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Obrišite senzor i probajte ponovo"</string>
- <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Jako pritisnite senzor"</string>
+ <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Čvrsto pritisnite senzor"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Previše sporo ste pomerili prst. Probajte ponovo."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Probajte sa drugim otiskom prsta"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Previše je svetlo"</string>
@@ -1851,7 +1852,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Da biste izašli, prevucite nadole odozgo."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Važi"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotirajte radi boljeg prikaza"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Izađite iz podeljenog ekrana radi boljeg prikaza"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Otvorite aplikaciju <xliff:g id="NAME">%s</xliff:g> preko celog ekrana da biste bolje videli"</string>
<string name="done_label" msgid="7283767013231718521">"Gotovo"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Kružni klizač za sate"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Kružni klizač za minute"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 7e24c7bfc9c6..f226b966bb38 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -622,16 +622,17 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Біяметрычнае абсталяванне недаступнае"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Аўтэнтыфікацыя скасавана"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Не распазнана"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Твар не распазнаны"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Аўтэнтыфікацыя скасавана"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Не заданы PIN-код, узор разблакіроўкі або пароль"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Памылка аўтэнтыфікацыі"</string>
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Ужываць блакіроўку экрана"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Каб працягнуць, скарыстайце свой сродак блакіроўкі экрана"</string>
- <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Моцна націсніце на сканер"</string>
+ <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Шчыльна прыкладзіце палец да сканера"</string>
<string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Не ўдалося распазнаць адбітак пальца. Паўтарыце спробу."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Ачысціце сканер адбіткаў пальцаў і паўтарыце спробу"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Ачысціце сканер і паўтарыце спробу"</string>
- <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Моцна націсніце на сканер"</string>
+ <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Шчыльна прыкладзіце палец да сканера"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Палец рухаўся занадта павольна. Паспрабуйце яшчэ раз."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Паспрабуйце іншы адбітак пальца"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Занадта светла"</string>
@@ -1852,7 +1853,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Для выхаду правядзіце зверху ўніз."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Зразумела"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Павярнуць для лепшага прагляду"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Выйсці з рэжыму падзеленага экрана для лепшага прагляду"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Для больш зручнага прагляду адкрыйце праграму \"<xliff:g id="NAME">%s</xliff:g>\", выкарыстоўваючы поўнаэкранны рэжым"</string>
<string name="done_label" msgid="7283767013231718521">"Гатова"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Кругавы паўзунок гадзін"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Кругавы паўзунок хвілін"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index d2afdb2981bb..f9c5bfd6e065 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометричният хардуер не е налице"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Удостоверяването бе анулирано"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Не е разпознато"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Лицето не е разпознато"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Удостоверяването бе анулирано"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Няма зададен ПИН код, фигура или парола"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Грешка при удостоверяването"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"За изход плъзнете пръст надолу от горната част."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Разбрах"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Завъртете за по-добър изглед"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Излезте от разделения екран за по-добър изглед"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Отворете <xliff:g id="NAME">%s</xliff:g> на цял екран за по-добър изглед"</string>
<string name="done_label" msgid="7283767013231718521">"Готово"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Кръгов плъзгач за часовете"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Кръгов плъзгач за минутите"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 42c00f4b043b..c339a1058d9c 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"বায়োমেট্রিক হার্ডওয়্যার পাওয়া যাবে না"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"যাচাইকরণ বাতিল হয়েছে"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"স্বীকৃত নয়"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"ফেস চেনা যায়নি"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"যাচাইকরণ বাতিল হয়েছে"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"পিন, প্যাটার্ন অথবা পাসওয়ার্ড সেট করা নেই"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"যাচাইকরণে সমস্যা হয়েছে"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"প্রস্থান করতে উপর থেকে নিচের দিকে সোয়াইপ করুন"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"বুঝেছি"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"আরও ভাল ক্যামেরা ভিউ পাওয়ার জন্য ঘোরান"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"আরও ভাল ক্যামেরা ভিউ পাওয়ার জন্য স্প্লিট স্ক্রিন থেকে বেরিয়ে আসুন"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"আরও ভাল ভিউয়ের জন্য ফুল স্ক্রিনে <xliff:g id="NAME">%s</xliff:g> খুলুন"</string>
<string name="done_label" msgid="7283767013231718521">"সম্পন্ন হয়েছে"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"বৃত্তাকার ঘণ্টা নির্বাচকের স্লাইডার"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"বৃত্তাকার মিনিট নির্বাচকের স্লাইডার"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 3e5700493c1b..85d0ed162a6a 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -621,13 +621,14 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrijski hardver nije dostupan"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentifikacija je otkazana"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nije prepoznato"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Lice nije prepoznato"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autentifikacija je otkazana"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nije postavljen PIN, uzorak niti lozinka"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Greška pri autentifikaciji"</string>
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Koristi zaključavanje ekrana"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Unesite zaključavanje ekrana da nastavite"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Čvrsto pritisnite senzor"</string>
- <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Nije moguće prepoznati otisak prsta. Pokušajte ponovo."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Otisak prsta nije prepoznat. Pokušajte ponovo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Očistite senzor za otisak prsta i pokušajte ponovo"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Očistite senzor i pokušajte ponovo"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Čvrsto pritisnite senzor"</string>
@@ -1851,7 +1852,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Da izađete, prevucite odozgo nadolje."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Razumijem"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotirajte za bolji prikaz"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Izađite iz podijeljenog ekrana za bolji prikaz"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Otvorite aplikaciju <xliff:g id="NAME">%s</xliff:g> preko cijelog ekrana radi boljeg pregleda"</string>
<string name="done_label" msgid="7283767013231718521">"Gotovo"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Kružni klizač za odabir sata"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Kružni klizač za minute"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 622d648943a3..d448ebf733c0 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -621,6 +621,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Maquinari biomètric no disponible"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"S\'ha cancel·lat l\'autenticació"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"No s\'ha reconegut"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"No s\'ha reconegut la cara"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"S\'ha cancel·lat l\'autenticació"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"No s\'ha definit cap PIN, patró o contrasenya"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Error en l\'autenticació"</string>
@@ -1851,7 +1852,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Per sortir, llisca cap avall des de la part superior."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Entesos"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Gira per a una millor visualització"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Surt de la pantalla dividida per a una millor visualització"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Obre <xliff:g id="NAME">%s</xliff:g> en pantalla completa per a una millor visualització"</string>
<string name="done_label" msgid="7283767013231718521">"Fet"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Control circular de les hores"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Control circular dels minuts"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 01f4572345b6..6aabea5e15ee 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -622,6 +622,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrický hardware není k dispozici"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Ověření bylo zrušeno"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nerozpoznáno"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Obličej nebyl rozpoznán"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Ověření bylo zrušeno"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Není nastaven žádný PIN, gesto ani heslo"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Při ověřování došlo k chybě"</string>
@@ -1852,7 +1853,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Režim ukončíte přejetím prstem shora dolů."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Rozumím"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Otočte obrazovku, abyste lépe viděli"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Ukončete režim rozdělené obrazovky, abyste lépe viděli"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Otevřete aplikaci <xliff:g id="NAME">%s</xliff:g> na celou obrazovku, aby byla lépe vidět"</string>
<string name="done_label" msgid="7283767013231718521">"Hotovo"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Kruhový posuvník hodin"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Kruhový posuvník minut"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index ebd38c89cfcf..2eecd2739a64 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrisk hardware er ikke tilgængelig"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Godkendelsen blev annulleret"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Ikke genkendt"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Ansigt blev ikke genkendt"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Godkendelsen blev annulleret"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Der er ikke angivet pinkode, mønster eller adgangskode"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Der opstod fejl i forbindelse med godkendelse"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Stryg ned fra toppen for at afslutte."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"OK, det er forstået"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Roter, og få en bedre visning"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Afslut opdelt skærm, og få en bedre visning"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Åbn <xliff:g id="NAME">%s</xliff:g> i fuld skærm for at få en bedre visning"</string>
<string name="done_label" msgid="7283767013231718521">"Udfør"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Cirkulær timevælger"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Cirkulær minutvælger"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 4441580842b4..ad6cfbf6042e 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrische Hardware nicht verfügbar"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentifizierung abgebrochen"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nicht erkannt"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Gesicht nicht erkannt"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Authentifizierung abgebrochen"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Keine PIN, kein Muster und kein Passwort festgelegt"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Fehler bei der Authentifizierung"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Zum Beenden von oben nach unten wischen"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Ok"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Drehen, um die Ansicht zu verbessern"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Splitscreen-Modus beenden, um die Ansicht zu verbessern"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Du kannst <xliff:g id="NAME">%s</xliff:g> im Vollbildmodus öffnen, um die Ansicht zu verbessern"</string>
<string name="done_label" msgid="7283767013231718521">"Fertig"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Kreisförmiger Schieberegler für Stunden"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Kreisförmiger Schieberegler für Minuten"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 7a0f5c0520e9..9b0bc2891ce7 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -620,13 +620,14 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Δεν υπάρχει διαθέσιμος βιομετρικός εξοπλισμός"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Ο έλεγχος ταυτότητας ακυρώθηκε"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Δεν αναγνωρίστηκε"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Το πρόσωπο δεν αναγνωρίστηκε"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Ο έλεγχος ταυτότητας ακυρώθηκε"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Δεν έχει οριστεί PIN, μοτίβο ή κωδικός πρόσβασης"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Σφάλμα κατά τον έλεγχο ταυτότητας"</string>
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Χρήση κλειδώματος οθόνης"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Χρησιμοποιήστε το κλείδωμα οθόνης για να συνεχίσετε"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Πιέστε σταθερά τον αισθητήρα"</string>
- <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Δεν είναι δυνατή η αναγνώριση του δακτυλικού αποτυπώματος. Δοκιμάστε ξανά."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"To δακτ. αποτύπωμα δεν αναγνωρίστηκε. Δοκιμάστε ξανά."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Καθαρίστε τον αισθητήρα δακτυλικών αποτυπωμάτων και δοκιμάστε ξανά"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Καθαρίστε τον αισθητήρα και δοκιμάστε ξανά"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Πιέστε σταθερά τον αισθητήρα"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Για έξοδο, σύρετε προς τα κάτω από το επάνω μέρος."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Το κατάλαβα"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Περιστρέψτε την οθόνη για καλύτερη προβολή"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Εξέλθετε από τον διαχωρισμό οθόνης για καλύτερη προβολή"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Ανοίξτε την εφαρμογή <xliff:g id="NAME">%s</xliff:g> σε πλήρη οθόνη για καλύτερη προβολή"</string>
<string name="done_label" msgid="7283767013231718521">"Τέλος"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Κυκλικό ρυθμιστικό ωρών"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Κυκλικό ρυθμιστικό λεπτών"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 725f9fa3d00a..675add93a146 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometric hardware unavailable"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentication cancelled"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Not recognised"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Face not recognised"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Authentication cancelled"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"No pin, pattern or password set"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Error while authenticating"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"To exit, swipe down from the top."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Got it"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotate for a better view"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Exit split screen for a better view"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Open <xliff:g id="NAME">%s</xliff:g> in full screen for a better view"</string>
<string name="done_label" msgid="7283767013231718521">"Done"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Hours circular slider"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Minutes circular slider"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 86e1267eca9d..7b6ccf31f886 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometric hardware unavailable"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentication canceled"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Not recognized"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Face not recognized"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Authentication canceled"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"No pin, pattern, or password set"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Error authenticating"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"To exit, swipe down from the top."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Got it"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotate for a better view"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Exit split screen for a better view"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Open <xliff:g id="NAME">%s</xliff:g> in full screen for a better view"</string>
<string name="done_label" msgid="7283767013231718521">"Done"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Hours circular slider"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Minutes circular slider"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index f0af9de9e6a9..a1c7872e5110 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometric hardware unavailable"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentication cancelled"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Not recognised"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Face not recognised"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Authentication cancelled"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"No pin, pattern or password set"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Error while authenticating"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"To exit, swipe down from the top."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Got it"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotate for a better view"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Exit split screen for a better view"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Open <xliff:g id="NAME">%s</xliff:g> in full screen for a better view"</string>
<string name="done_label" msgid="7283767013231718521">"Done"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Hours circular slider"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Minutes circular slider"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 2677a55b7aeb..7c467e8c841f 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometric hardware unavailable"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentication cancelled"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Not recognised"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Face not recognised"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Authentication cancelled"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"No pin, pattern or password set"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Error while authenticating"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"To exit, swipe down from the top."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Got it"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotate for a better view"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Exit split screen for a better view"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Open <xliff:g id="NAME">%s</xliff:g> in full screen for a better view"</string>
<string name="done_label" msgid="7283767013231718521">"Done"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Hours circular slider"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Minutes circular slider"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index fc93b3cccc43..4f6c12069810 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‎‏‏‏‎‎‏‎‏‏‏‏‏‎‎‎‏‎‏‎‎‏‏‏‎‏‎‏‎‏‎‏‏‏‎‏‎‏‏‏‎‎‏‎‏‎‎‎‏‏‎‎‎‎‎Biometric hardware unavailable‎‏‎‎‏‎"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‏‏‎‏‏‏‏‏‎‎‏‎‏‏‎‎‎‏‎‏‏‏‏‎‎‎‎‏‏‎‎‎‏‏‎‎‎‎‎‏‏‎‎‏‎‎‏‎‎‎‏‎‎Authentication canceled‎‏‎‎‏‎"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‏‎‏‎‎‏‏‎‎‏‎‏‎‏‏‎‏‎‏‏‎‎‏‎‎‏‏‎‏‏‎‎‎‎‏‏‎‏‎‎‎‏‎‏‏‏‎‎‎‎‎Not recognized‎‏‎‎‏‎"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‎‏‎‎‏‎‎‏‏‎‎‏‏‎‎‏‏‎‎‎‎‎‎‏‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‎‎‏‎‎‎‎‎Face not recognized‎‏‎‎‏‎"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‏‎‎‎‏‏‎‎‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‎‎‏‏‎‎‏‏‏‎‏‏‎‏‎‏‎‎‎‏‎‏‎‎‎‎‏‎‎Authentication canceled‎‏‎‎‏‎"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‎‏‏‏‏‎‏‏‏‎‎‎‏‏‎‎‎‎‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‏‎‏‎‎‎‏‎‏‎‎‎No pin, pattern, or password set‎‏‎‎‏‎"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‏‏‎‏‏‏‎‏‏‏‎‎‏‎‏‎‎‏‏‎‎‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‎‎‏‏‎‏‎‎‎‏‏‏‎Error authenticating‎‏‎‎‏‎"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎‏‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‎‏‎‎‎‏‏‏‎‎‏‏‎‎‎‎‎‎To exit, swipe down from the top.‎‏‎‎‏‎"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‎‏‎‎‎‎‎‏‏‎‎‎‎‎‏‏‎‏‏‎Got it‎‏‎‎‏‎"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‎‎‏‎‏‏‏‎‎‎‎‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‎‏‏‏‏‎‎Rotate for a better view‎‏‎‎‏‎"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‎‎‎‎‎‎‎‎‎‏‏‏‎‏‎‏‎‎‏‎‏‎‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎‏‎‎Exit split screen for a better view‎‏‎‎‏‎"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‎‏‎‎‏‎‎‏‎‎‏‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‏‏‎‏‎‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‎Open ‎‏‎‎‏‏‎<xliff:g id="NAME">%s</xliff:g>‎‏‎‎‏‏‏‎ in full screen for a better view‎‏‎‎‏‎"</string>
<string name="done_label" msgid="7283767013231718521">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‏‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎‎‎‎‎‏‏‏‏‎‏‎‏‎‎‏‏‎‎‎‏‎‎‏‏‏‎‎‎‎‎‏‏‏‏‎‎‏‎Done‎‏‎‎‏‎"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‎‎‎‎‏‎‏‏‏‎‏‎‎‏‏‎‎‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‏‎‏‎‎‏‎‎‎‏‏‏‏‎‏‎‎‏‏‎‏‏‎Hours circular slider‎‏‎‎‏‎"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‎‎‏‏‎‏‎‎‎‏‎‎‏‎‏‏‎‏‏‏‎‏‎‏‎‏‎‎‎‏‎‏‏‏‏‏‏‎‎Minutes circular slider‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 2bf0e3618c5c..99ab4509fabc 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -621,6 +621,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"No hay hardware biométrico disponible"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Se canceló la autenticación"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"No se reconoció"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"No se reconoció el rostro"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Se canceló la autenticación"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"No se estableció ningún PIN, patrón ni contraseña"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Error de autenticación"</string>
@@ -1257,7 +1258,7 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Iniciando aplicaciones"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finalizando el inicio"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Presionaste el botón de encendido. Por lo general, esta acción apaga la pantalla.\n\nPresiona suavemente mientras configuras tu huella dactilar."</string>
- <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Para finalizar, apaga la pantalla"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Para salir, apaga la pantalla"</string>
<string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Apagar"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"¿Verificar huella dactilar?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Presionaste el botón de encendido. Por lo general, esta acción apaga la pantalla.\n\nPresiona suavemente para verificar tu huella dactilar."</string>
@@ -1851,7 +1852,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Para salir, desliza el dedo hacia abajo desde la parte superior."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Entendido"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Gira la pantalla para obtener una mejor vista"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Sal de la pantalla dividida para obtener una mejor vista"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Abre <xliff:g id="NAME">%s</xliff:g> en pantalla completa para una mejor visualización"</string>
<string name="done_label" msgid="7283767013231718521">"Listo"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Control deslizante circular de horas"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Control deslizante circular de minutos"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 69128d9bdd48..6fcf445cce6c 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -621,6 +621,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biométrico no disponible"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autenticación cancelada"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"No se reconoce"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Cara no reconocida"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autenticación cancelada"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"No se ha definido el PIN, el patrón o la contraseña"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"No se ha podido autenticar"</string>
@@ -1371,7 +1372,7 @@
<string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Se ha detectado un accesorio de audio analógico"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"El dispositivo adjunto no es compatible con este teléfono. Toca para obtener más información."</string>
<string name="adb_active_notification_title" msgid="408390247354560331">"Depuración por USB activa"</string>
- <string name="adb_active_notification_message" msgid="5617264033476778211">"Tocar para desactivar depuración USB"</string>
+ <string name="adb_active_notification_message" msgid="5617264033476778211">"Toca para desactivar depuración USB"</string>
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Seleccionar para inhabilitar la depuración por USB"</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Depuración inalámbrica conectada"</string>
<string name="adbwifi_active_notification_message" msgid="930987922852867972">"Toca para desactivar la depuración inalámbrica"</string>
@@ -1851,7 +1852,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Para salir, desliza el dedo de arriba abajo."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Entendido"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Gira la pantalla para verlo mejor"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Sal de la pantalla dividida para verlo mejor"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Abre <xliff:g id="NAME">%s</xliff:g> en pantalla completa para verlo mejor"</string>
<string name="done_label" msgid="7283767013231718521">"Hecho"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Control deslizante circular de horas"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Control deslizante circular de minutos"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 0197f21e2b57..384280ea6916 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biomeetriline riistvara ei ole saadaval"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentimine tühistati"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Ei tuvastatud"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Nägu ei tuvastatud"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autentimine tühistati"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"PIN-koodi, mustrit ega parooli pole määratud"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Viga autentimisel"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Väljumiseks pühkige ülevalt alla."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Selge"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Pöörake parema vaate jaoks"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Parema vaate jaoks väljuge jagatud ekraanikuvast"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Parema vaate jaoks avage rakendus <xliff:g id="NAME">%s</xliff:g> täisekraanil"</string>
<string name="done_label" msgid="7283767013231718521">"Valmis"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Ringikujuline tunniliugur"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Ringikujuline minutiliugur"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index f14a0070177f..51f41ef5535b 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biometrikoa ez dago erabilgarri"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Utzi da autentifikazioa"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Ez da ezagutu"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Ez da ezagutu aurpegia"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Utzi egin da autentifikazioa"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Ez da ezarri PIN koderik, eredurik edo pasahitzik"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Errorea autentifikatzean"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Irteteko, pasatu hatza goitik behera."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Ados"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Biratu pantaila ikuspegi hobea lortzeko"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Irten pantaila zatitutik ikuspegi hobea lortzeko"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Ireki <xliff:g id="NAME">%s</xliff:g> pantaila osoan eta ikuspegi hobea lortuko duzu"</string>
<string name="done_label" msgid="7283767013231718521">"Eginda"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Ordua aukeratzeko ikuspegi zirkularra"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Minutuak aukeratzeko ikuspegi zirkularra"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 74332ecc5439..4661f60c9c79 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"سخت‌افزار زیست‌سنجی دردسترس نیست"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"اصالت‌سنجی لغو شد"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"شناسایی نشد"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"چهره شناسایی نشد"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"اصالت‌سنجی لغو شد"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"پین، الگو یا گذرواژه‌ای تنظیم نشده است"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"خطا هنگام اصالت‌سنجی"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"برای خروج، انگشتتان را از بالای صفحه به پایین بکشید."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"متوجه شدم"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"برای دید بهتر، دستگاه را بچرخانید"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"برای دید بهتر، از صفحهٔ دونیمه خارج شوید"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"برای دید بهتر، <xliff:g id="NAME">%s</xliff:g> را به‌صورت تمام‌صفحه باز کنید"</string>
<string name="done_label" msgid="7283767013231718521">"تمام"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"لغزنده دایره‌ای ساعت"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"لغزنده دایره‌ای دقیقه"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index a5a0a3f7d9b3..6741605c19aa 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrinen laitteisto ei käytettävissä"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Todennus peruutettu"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Ei tunnistettu"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Kasvoja ei tunnistettu"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Todennus peruutettu"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"PIN-koodia, kuviota tai salasanaa ei ole asetettu"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Virhe todennuksessa"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Sulje palkki pyyhkäisemällä alas ruudun ylälaidasta."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Selvä"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Kierrä, niin saat paremman näkymän"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Poistu jaetulta näytöltä, niin saat paremman näkymän"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Avaa <xliff:g id="NAME">%s</xliff:g>, niin saat paremman näkymän"</string>
<string name="done_label" msgid="7283767013231718521">"Valmis"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Tuntien ympyränmuotoinen liukusäädin"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Minuuttien ympyränmuotoinen liukusäädin"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index da9399298d18..7a3bf7657195 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -621,6 +621,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Matériel biométrique indisponible"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentification annulée"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Données biométriques non reconnues"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Visage non reconnu"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Authentification annulée"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Aucun NIP, schéma ou mot de passe défini"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Erreur d\'authentification"</string>
@@ -1851,7 +1852,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Pour quitter, balayez vers le bas à partir du haut."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Faire pivoter pour obtenir un meilleur affichage"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Quitter l\'écran partagé pour obtenir un meilleur affichage"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Ouvrir <xliff:g id="NAME">%s</xliff:g> en plein écran pour un meilleur affichage"</string>
<string name="done_label" msgid="7283767013231718521">"Terminé"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Curseur circulaire des heures"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Curseur circulaire des minutes"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index f80373d07f01..d5d52e449c56 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -621,6 +621,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Matériel biométrique indisponible"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Authentification annulée"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Non reconnue"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Visage non reconnu"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Authentification annulée"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Aucun code, schéma ni mot de passe n\'est défini"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Erreur d\'authentification"</string>
@@ -1851,7 +1852,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Pour quitter, balayez l\'écran du haut vers le bas."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Faites pivoter pour mieux voir"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Quittez l\'écran partagé pour mieux voir"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Ouvrez <xliff:g id="NAME">%s</xliff:g> en plein écran pour mieux voir"</string>
<string name="done_label" msgid="7283767013231718521">"OK"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Curseur circulaire des heures"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Curseur circulaire des minutes"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 7aa45be7d126..16b1e002c258 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"O hardware biométrico non está dispoñible"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Cancelouse a autenticación"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Non se recoñeceu"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Non se recoñeceu a cara"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Cancelouse a autenticación"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Non se estableceu ningún PIN, padrón ou contrasinal"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Produciuse un erro ao realizar a autenticación"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Para saír, pasa o dedo cara abaixo desde a parte superior."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Entendido"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Xira a pantalla para que se vexa mellor"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Sae da pantalla dividida para que se vexa mellor"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Abre <xliff:g id="NAME">%s</xliff:g> en pantalla completa para unha mellor visualización"</string>
<string name="done_label" msgid="7283767013231718521">"Feito"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Control desprazable circular das horas"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Control desprazable circular dos minutos"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 9bc39626d133..703bb440f403 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"બાયોમેટ્રિક હાર્ડવેર ઉપલબ્ધ નથી"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"પ્રમાણીકરણ રદ કર્યું"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"ઓળખાયેલ નથી"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"ચહેરો ઓળખાયો નથી"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"પ્રમાણીકરણ રદ કર્યું"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"કોઈ પિન, પૅટર્ન અથવા પાસવર્ડ સેટ કરેલો નથી"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"પ્રમાણિત કરવામાં ભૂલ આવી"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"બહાર નીકળવા માટે, ટોચ પરથી નીચે સ્વાઇપ કરો."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"સમજાઈ ગયું"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"બહેતર વ્યૂ માટે ફેરવો"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"બહેતર વ્યૂ માટે, વિભાજિત સ્ક્રીનમાંથી બહાર નીકળો"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"બહેતર વ્યૂ માટે, પૂર્ણ સ્ક્રીનમાં <xliff:g id="NAME">%s</xliff:g> ખોલો"</string>
<string name="done_label" msgid="7283767013231718521">"થઈ ગયું"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"કલાકનું વર્તુળાકાર સ્લાઇડર"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"મિનિટનું વર્તુળાકાર સ્લાઇડર"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 9ebe4f70c3e1..2caba460a8f5 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"बायोमेट्रिक हार्डवेयर उपलब्ध नहीं है"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"प्रमाणीकरण रद्द किया गया"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"पहचान नहीं हो पाई"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"चेहरा नहीं पहचाना गया"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"प्रमाणीकरण रद्द किया गया"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"पिन, पैटर्न या पासवर्ड सेट नहीं है"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"गड़बड़ी की पुष्टि की जा रही है"</string>
@@ -1715,7 +1716,7 @@
<string name="color_correction_feature_name" msgid="7975133554160979214">"रंग में सुधार करने की सुविधा"</string>
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"वन-हैंडेड मोड"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"स्क्रीन की रोशनी को सामान्य लेवल से और कम करने की सुविधा"</string>
- <string name="hearing_aids_feature_name" msgid="1125892105105852542">"सुनने में मदद करने वाले डिवाइस"</string>
+ <string name="hearing_aids_feature_name" msgid="1125892105105852542">"कान की मशीन"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"आवाज़ कम-ज़्यादा करने वाले दोनों बटन दबाकर रखें. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> को चालू कर दिया गया."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"आवाज़ कम-ज़्यादा करने वाले दोनों बटन दबाकर रखें. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> को बंद कर दिया गया."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"आवाज़ बटन को छोड़ें. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> की सुविधा चालू करने के लिए, आवाज़ वाले दोनों बटन तीन सेकंड तक दबाकर रखें."</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"बाहर निकलने के लिए, ऊपर से नीचे स्वा‍इप करें."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"ठीक है"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"बेहतर व्यू पाने के लिए, डिवाइस की स्क्रीन को घुमाएं"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"बेहतर व्यू पाने के लिए, स्प्लिट स्क्रीन मोड बंद करें"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"बेहतर व्यू पाने के लिए <xliff:g id="NAME">%s</xliff:g> को फ़ुल स्क्रीन में खोलें"</string>
<string name="done_label" msgid="7283767013231718521">"हो गया"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"घंटो का चक्राकार स्लाइडर"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"मिनटों का चक्राकार स्लाइडर"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 4ba22b28bcbc..d655dd4beed0 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -161,7 +161,7 @@
<string name="httpErrorLookup" msgid="3099834738227549349">"URL nije moguće pronaći."</string>
<string name="httpErrorUnsupportedAuthScheme" msgid="3976195595501606787">"Shema autentifikacije web-lokacije nije podržana."</string>
<string name="httpErrorAuth" msgid="469553140922938968">"Autentifikacija nije moguća."</string>
- <string name="httpErrorProxyAuth" msgid="7229662162030113406">"Provjera autentičnosti preko proxy poslužitelja nije bila uspješna."</string>
+ <string name="httpErrorProxyAuth" msgid="7229662162030113406">"Autentifikacija preko proxy poslužitelja nije bila uspješna."</string>
<string name="httpErrorConnect" msgid="3295081579893205617">"Povezivanje s poslužiteljem nije moguće."</string>
<string name="httpErrorIO" msgid="3860318696166314490">"Komunikacija s poslužiteljem nije moguća. Pokušajte ponovno kasnije."</string>
<string name="httpErrorTimeout" msgid="7446272815190334204">"Veza s poslužiteljem privremeno je zaustavljena."</string>
@@ -621,6 +621,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrijski hardver nije dostupan"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentifikacija otkazana"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nije prepoznato"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Lice nije prepoznato"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autentifikacija otkazana"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nisu postavljeni PIN, uzorak ni zaporka"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Pogreška prilikom autentifikacije"</string>
@@ -1851,7 +1852,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Za izlaz prijeđite prstom od vrha prema dolje."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Shvaćam"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Zakrenite kako biste bolje vidjeli"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Zatvorite podijeljeni zaslon kako biste bolje vidjeli"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Otvorite aplikaciju <xliff:g id="NAME">%s</xliff:g> preko cijelog zaslona za bolji prikaz"</string>
<string name="done_label" msgid="7283767013231718521">"Gotovo"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Kružni klizač sati"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Kružni klizač minuta"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 38fd4ac989a2..e5efd16f3b3c 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrikus hardver nem áll rendelkezésre"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Hitelesítés megszakítva"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nem ismerhető fel"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Sikertelen arcfelismerés"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Hitelesítés megszakítva"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nem állított be PIN-kódot, mintát vagy jelszót."</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Hiba történt a hitelesítés közben"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Kilépéshez csúsztassa ujját fentről lefelé."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Értem"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Forgassa el a jobb élmény érdekében"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Lépjen ki az osztott képernyős módból a jobb élmény érdekében"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"A jobb élmény érdekében teljes képernyős módban nyissa meg a(z) <xliff:g id="NAME">%s</xliff:g> alkalmazást"</string>
<string name="done_label" msgid="7283767013231718521">"Kész"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Óra kör alakú csúszkája"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Perc kör alakú csúszkája"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 272dfee4002f..569c54f271b9 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Կենսաչափական սարքը հասանելի չէ"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Նույնականացումը չեղարկվեց"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Չհաջողվեց ճանաչել"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Դեմքը չի ճանաչվել"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Նույնականացումը չեղարկվեց"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Ավելացրեք PIN կոդ, նախշ կամ գաղտնաբառ։"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Չհաջողվեց նույնականացնել"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Դուրս գալու համար վերևից սահահարվածեք դեպի ներքև:"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Պարզ է"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Պտտեք՝ դիտակերպը լավացնելու համար"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Դուրս եկեք կիսված էկրանի ռեժիմից՝ դիտակերպը լավացնելու համար"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Բացեք <xliff:g id="NAME">%s</xliff:g> հավելվածը լիաէկրան ռեժիմում՝ դիտակերպը լավացնելու համար"</string>
<string name="done_label" msgid="7283767013231718521">"Պատրաստ է"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Ժամերի ընտրություն թվատախտակից"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Րոպեների ընտրություն թվատախտակից"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index c854e12fa4e3..f382b120b617 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biometrik tidak tersedia"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentikasi dibatalkan"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Tidak dikenali"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Wajah tidak dikenali"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autentikasi dibatalkan"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Tidak ada PIN, pola, atau sandi yang disetel"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Error saat mengautentikasi"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Untuk keluar, geser layar ke bawah dari atas."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Mengerti"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Putar posisi layar untuk mendapatkan tampilan yang lebih baik"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Keluar dari layar terpisah untuk mendapatkan tampilan yang lebih baik"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Buka <xliff:g id="NAME">%s</xliff:g> dalam layar penuh untuk mendapatkan tampilan yang lebih baik"</string>
<string name="done_label" msgid="7283767013231718521">"Selesai"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Penggeser putar jam"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Penggeser putar menit"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 1c7a07df45a8..1c7f5b3e0352 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Lífkennavélbúnaður ekki tiltækur"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Hætt við auðkenningu"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Þekktist ekki"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Andlit þekkist ekki"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Hætt við auðkenningu"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Ekkert PIN-númer, mynstur eða aðgangsorð stillt"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Villa við auðkenningu"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Strjúktu niður frá efri brún til að hætta."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Ég skil"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Snúðu til að sjá betur"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Lokaðu skjáskiptingu til að sjá betur"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Opnaðu <xliff:g id="NAME">%s</xliff:g> á öllum skjánum til að fá betra yfirlit"</string>
<string name="done_label" msgid="7283767013231718521">"Lokið"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Valskífa fyrir klukkustundir"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Valskífa fyrir mínútur"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 2e9d1ec2158c..705613ad6fad 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -621,6 +621,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biometrico non disponibile"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autenticazione annullata"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Non riconosciuto"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Volto non riconosciuto"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autenticazione annullata"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Non hai impostato PIN, sequenza o password"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Errore durante l\'autenticazione"</string>
@@ -1851,7 +1852,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Per uscire, scorri dall\'alto verso il basso."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Ruota per migliorare l\'anteprima"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Esci dallo schermo diviso per migliorare l\'anteprima"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Apri <xliff:g id="NAME">%s</xliff:g> a schermo intero per migliorare la visualizzazione"</string>
<string name="done_label" msgid="7283767013231718521">"Fine"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Cursore circolare per le ore"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Cursore circolare per i minuti"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 84f56ff54f36..e4b50f9f4b86 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -621,6 +621,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"חומרה ביומטרית לא זמינה"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"האימות בוטל"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"לא זוהתה"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"הפנים לא זוהו"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"האימות בוטל"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"עוד לא הוגדרו קוד אימות, קו ביטול נעילה או סיסמה"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"שגיאה באימות"</string>
@@ -1851,7 +1852,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"כדי לצאת, פשוט מחליקים אצבע מלמעלה למטה."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"הבנתי"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"מסובבים כדי לראות טוב יותר"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"צריך לצאת מהמסך המפוצל כדי לראות טוב יותר"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"צריך לפתוח את <xliff:g id="NAME">%s</xliff:g> במסך מלא כדי לראות טוב יותר"</string>
<string name="done_label" msgid="7283767013231718521">"סיום"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"מחוון שעות מעגלי"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"מחוון דקות מעגלי"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index dc4694d03bed..64eb303c2e20 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"生体認証ハードウェアが利用できません"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"認証をキャンセルしました"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"認識されませんでした"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"顔を認識できません"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"認証をキャンセルしました"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"PIN、パターン、パスワードが設定されていません"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"エラー認証"</string>
@@ -648,8 +649,8 @@
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"指紋の設定がタイムアウトしました。もう一度お試しください。"</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"指紋の操作をキャンセルしました。"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"指紋の操作がユーザーによりキャンセルされました。"</string>
- <string name="fingerprint_error_lockout" msgid="6626753679019351368">"試行回数が上限を超えました。代わりに PIN を入力してください。"</string>
- <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"試行回数が上限を超えました。代わりに PIN を入力してください。"</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"試行回数が上限を超えました。代わりに画面ロックを使用してください。"</string>
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"試行回数が上限を超えました。代わりに画面ロックを使用してください。"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"指紋を処理できません。もう一度お試しください。"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"指紋が登録されていません。"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"このデバイスには指紋認証センサーがありません。"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"終了するには、上から下にスワイプします。"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"画面を回転させて見やすくしましょう"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"分割画面を終了して見やすくしましょう"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"<xliff:g id="NAME">%s</xliff:g> を全画面表示で開いて見やすくしましょう"</string>
<string name="done_label" msgid="7283767013231718521">"完了"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"円形スライダー(時)"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"円形スライダー(分)"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 13d430c985fe..65bdb2553bab 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ბიომეტრიული აპარატურა მიუწვდომელია"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"ავტორიზაცია გაუქმდა"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"არ არის ამოცნობილი"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"სახის ამოცნობა ვერ მოხერხდა"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"ავტორიზაცია გაუქმდა"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"PIN-კოდი, ნიმუში ან პაროლი დაყენებული არ არის"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"შეცდომა ავთენტიკაციისას"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"გამოსვლისათვის, გაასრიალეთ ზემოდან ქვემოთ."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"გასაგებია"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"შეატრიალეთ უკეთესი ხედისთვის"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"უკეთესი ხედვისთვის გამოდით გაყოფილი ეკრანიდან"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"გახსენით <xliff:g id="NAME">%s</xliff:g> სრულ ეკრანზე უკეთესი ხედისთვის"</string>
<string name="done_label" msgid="7283767013231718521">"დასრულდა"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"საათების წრიული სლაიდერი"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"წუთების წრიული სლაიდერი"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 03897c1e90f7..d2bc2ca53bdb 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометрикалық жабдық жоқ"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Аутентификациядан бас тартылды."</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Танылмады"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Бет танылмады."</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Аутентификациядан бас тартылды."</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Ешқандай PIN коды, өрнек немесе құпия сөз орнатылмаған."</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Аутентификациялауда қате шықты."</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Шығу үшін жоғарыдан төмен қарай сырғытыңыз."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Түсінікті"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Жақсырақ көру үшін бұрыңыз."</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Жақсырақ көру үшін экранды бөлу режимінен шығыңыз."</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Жақсырақ көру үшін <xliff:g id="NAME">%s</xliff:g> қолданбасын толық экранда ашыңыз."</string>
<string name="done_label" msgid="7283767013231718521">"Дайын"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Сағаттар айналымының қозғалтқышы"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Минут айналымын қозғалтқыш"</string>
@@ -1943,7 +1944,7 @@
<string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Ұсынылатын аймақтар"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Ұсынылған тілдер"</string>
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Ұсынылған аймақтар"</string>
- <string name="language_picker_section_all" msgid="1985809075777564284">"Барлық тілдер"</string>
+ <string name="language_picker_section_all" msgid="1985809075777564284">"Барлық тіл"</string>
<string name="region_picker_section_all" msgid="756441309928774155">"Барлық аймақтар"</string>
<string name="locale_search_menu" msgid="6258090710176422934">"Іздеу"</string>
<string name="app_suspended_title" msgid="888873445010322650">"Қолданба қолжетімді емес"</string>
@@ -2289,7 +2290,7 @@
<string name="window_magnification_prompt_title" msgid="2876703640772778215">"Жаңа ұлғайту параметрлері"</string>
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"Енді экранның бір бөлігін ұлғайтуға болады."</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Параметрлер бөлімінен қосу"</string>
- <string name="dismiss_action" msgid="1728820550388704784">"Қабылдамау"</string>
+ <string name="dismiss_action" msgid="1728820550388704784">"Жабу"</string>
<string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Құрылғы микрофонын блоктан шығарыңыз"</string>
<string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Құрылғы камерасын блоктан шығарыңыз"</string>
<string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"&lt;b&gt;<xliff:g id="APP">%s</xliff:g>&lt;/b&gt; және барлық қолданбалар мен қызметтерге арналған."</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index d7f979f49ee5..7bc1a200883a 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"មិនអាច​ប្រើឧបករណ៍​ស្កេន​ស្នាមម្រាមដៃ​បានទេ"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"បាន​បោះបង់​ការ​ផ្ទៀងផ្ទាត់"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"មិនអាចសម្គាល់បានទេ"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"មិន​ស្គាល់​មុខ"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"បាន​បោះបង់​ការ​ផ្ទៀងផ្ទាត់"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"គ្មាន​ការកំណត់​កូដ pin លំនាំ ឬពាក្យសម្ងាត់​ទេ"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"មានបញ្ហាក្នុង​ការផ្ទៀងផ្ទាត់"</string>
@@ -683,7 +684,7 @@
<string name="face_acquired_too_far" msgid="2922278214231064859">"ដាក់​ទូរសព្ទ​ឱ្យជិត​ជាងមុន"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"រំកិល​ទូរសព្ទឡើងលើ"</string>
<string name="face_acquired_too_low" msgid="4075391872960840081">"រំកិល​ទូរសព្ទចុះក្រោម"</string>
- <string name="face_acquired_too_right" msgid="6245286514593540859">"ដាក់​ទូរសព្ទ​ទៅខាងឆ្វេងអ្នក"</string>
+ <string name="face_acquired_too_right" msgid="6245286514593540859">"រំកិលទូរសព្ទ​ទៅខាងឆ្វេង"</string>
<string name="face_acquired_too_left" msgid="9201762240918405486">"រំកិលទូរសព្ទ​ទៅខាងស្ដាំ"</string>
<string name="face_acquired_poor_gaze" msgid="4427153558773628020">"សូមមើល​ឱ្យចំ​ឧបករណ៍​របស់អ្នក​ជាងមុន។"</string>
<string name="face_acquired_not_detected" msgid="1057966913397548150">"មើលមិនឃើញ​មុខរបស់អ្នកទេ។ កាន់ទូរសព្ទរបស់អ្នក​ដាក់ត្រឹមភ្នែក។"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"ដើម្បីចាកចេញ សូមអូសពីលើចុះក្រោម។"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"យល់ហើយ"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"បង្វិលដើម្បីមើលបានកាន់តែច្បាស់"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"ចេញពីមុខងារ​បំបែកអេក្រង់ដើម្បីមើលបានកាន់តែច្បាស់"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"បើក <xliff:g id="NAME">%s</xliff:g> នៅក្នុងអេក្រង់ពេញ ដើម្បីមើលបានកាន់តែច្បាស់"</string>
<string name="done_label" msgid="7283767013231718521">"រួចរាល់"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"គ្រាប់​រំកិល​រង្វង់​ម៉ោង"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"គ្រាប់​រំកិល​រង្វង់​នាទី"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 55a0a8430122..1587918ed51a 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ಬಯೋಮೆಟ್ರಿಕ್ ಹಾರ್ಡ್‌ವೇರ್‌ ಲಭ್ಯವಿಲ್ಲ"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"ಪ್ರಮಾಣೀಕರಣವನ್ನು ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"ಮುಖವನ್ನು ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"ಪ್ರಮಾಣೀಕರಣವನ್ನು ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"ಪಿನ್, ಪ್ಯಾಟರ್ನ್ ಅಥವಾ ಪಾಸ್‌ವರ್ಡ್ ಸೆಟ್ ಮಾಡಿಲ್ಲ"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"ದೃಢೀಕರಿಸುವಾಗ ದೋಷ ಎದುರಾಗಿದೆ"</string>
@@ -1351,7 +1352,7 @@
<string name="time_picker_dialog_title" msgid="9053376764985220821">"ಸಮಯವನ್ನು ಹೊಂದಿಸಿ"</string>
<string name="date_picker_dialog_title" msgid="5030520449243071926">"ದಿನಾಂಕವನ್ನು ಹೊಂದಿಸಿ"</string>
<string name="date_time_set" msgid="4603445265164486816">"ಹೊಂದಿಸು"</string>
- <string name="date_time_done" msgid="8363155889402873463">"ಮುಗಿದಿದೆ"</string>
+ <string name="date_time_done" msgid="8363155889402873463">"ಆಯಿತು"</string>
<string name="perms_new_perm_prefix" msgid="6984556020395757087"><font size="12" fgcolor="#ff33b5e5">"ಹೊಸ: "</font></string>
<string name="perms_description_app" msgid="2747752389870161996">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಮೂಲಕ ಒದಗಿಸಲಾಗಿದೆ."</string>
<string name="no_permissions" msgid="5729199278862516390">"ಯಾವುದೇ ಅನುಮತಿಗಳ ಅಗತ್ಯವಿಲ್ಲ"</string>
@@ -1850,8 +1851,8 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"ನಿರ್ಗಮಿಸಲು, ಮೇಲಿನಿಂದ ಕೆಳಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"ತಿಳಿಯಿತು"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ಅತ್ಯುತ್ತಮ ವೀಕ್ಷಣೆಗಾಗಿ ತಿರುಗಿಸಿ"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"ಅತ್ಯುತ್ತಮ ವೀಕ್ಷಣೆಗಾಗಿ ಸ್ಪ್ಲಿಟ್‌ ಸ್ಕ್ರೀನ್‌ನಿಂದ ನಿರ್ಗಮಿಸಿ"</string>
- <string name="done_label" msgid="7283767013231718521">"ಮುಗಿದಿದೆ"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"ಅತ್ಯುತ್ತಮ ವೀಕ್ಷಣೆಗಾಗಿ <xliff:g id="NAME">%s</xliff:g> ಅನ್ನು ಪೂರ್ಣ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ತೆರೆಯಿರಿ"</string>
+ <string name="done_label" msgid="7283767013231718521">"ಆಯಿತು"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"ಗಂಟೆಗಳ ವೃತ್ತಾಕಾರ ಸ್ಲೈಡರ್"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"ನಿಮಿಷಗಳ ವೃತ್ತಾಕಾರ ಸ್ಲೈಡರ್"</string>
<string name="select_hours" msgid="5982889657313147347">"ಗಂಟೆಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 93a7352eb7e0..2dabbf7377b1 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"생체 인식 하드웨어를 사용할 수 없음"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"인증이 취소되었습니다."</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"인식할 수 없음"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"얼굴을 인식할 수 없습니다."</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"인증이 취소되었습니다."</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"PIN, 패턴, 비밀번호가 설정되지 않음"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"인증 오류"</string>
@@ -635,7 +636,7 @@
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"너무 밝음"</string>
<string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"전원 누름이 감지되었습니다."</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"조정 시도"</string>
- <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"지문이 인식될 때마다 손가락을 조금씩 이동하세요"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"지문이 인식될 때마다 손가락의 위치를 조금씩 바꾸세요"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_error_not_match" msgid="4599441812893438961">"지문이 인식되지 않았습니다."</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"종료하려면 위에서 아래로 스와이프합니다."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"확인"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"카메라 미리보기 화면이 잘 보이도록 회전하세요."</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"카메라 미리보기 화면이 잘 보이도록 화면 분할을 종료하세요."</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"화면이 더 잘 보이도록 <xliff:g id="NAME">%s</xliff:g>을(를) 전체 화면에서 여세요."</string>
<string name="done_label" msgid="7283767013231718521">"완료"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"시간 원형 슬라이더"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"분 원형 슬라이더"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 5cd22384f59c..f4c887a6e101 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометрикалык аппарат жеткиликсиз"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Аныктыгын текшерүү жокко чыгарылды"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Таанылган жок"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Жүз таанылган жок"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Аныктыгын текшерүү жокко чыгарылды"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"PIN код, графикалык ачкыч же сырсөз коюлган жок"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Аутентификация катасы"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Чыгуу үчүн экранды ылдый сүрүп коюңуз."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Түшүндүм"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Жакшыраак көрүү үчүн буруңуз"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Жакшыраак көрүү үчүн экранды бөлүү режиминен чыгыңыз"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Жакшыраак көрүү үчүн <xliff:g id="NAME">%s</xliff:g> колдонмосун толук экранда ачыңыз"</string>
<string name="done_label" msgid="7283767013231718521">"Даяр"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Саат жебеси"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Мүнөт жебеси"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 269fa169bd43..b600d2edcbd1 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ຮາດແວຊີວະມິຕິບໍ່ສາມາດໃຊ້ໄດ້"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"ຍົກເລີກການຮັບຮອງຄວາມຖືກຕ້ອງແລ້ວ"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"ບໍ່ຮັບຮູ້"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"ບໍ່ສາມາດຈຳແນກໜ້າໄດ້"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"ຍົກເລີກການຮັບຮອງຄວາມຖືກຕ້ອງແລ້ວ"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"ບໍ່ໄດ້ຕັ້ງ PIN, ຮູບແບບປົດລັອກ ຫຼື ລະຫັດຜ່ານ"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"ເກີດຄວາມຜິດພາດໃນການພິສູດຢືນຢັນ"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"ຫາກຕ້ອງການອອກ, ໃຫ້ຮູດຈາກທາງເທິງລົງມາທາງລຸ່ມ."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"ໄດ້​ແລ້ວ"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ໝຸນເພື່ອມຸມມອງທີ່ດີຂຶ້ນ"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"ອອກຈາກແບ່ງໜ້າຈໍເພື່ອມຸມມອງທີ່ດີຂຶ້ນ"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"ເປີດ <xliff:g id="NAME">%s</xliff:g> ໃນໂໝດເຕັມຈໍເພື່ອມຸມມອງທີ່ດີຂຶ້ນ"</string>
<string name="done_label" msgid="7283767013231718521">"ແລ້ວໆ"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"ໂຕໝຸນປັບຊົ່ວໂມງ"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"ໂຕໝຸນປັບນາທີ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 4ae49ac1207d..e1188b570c10 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -622,6 +622,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrinė aparatinė įranga nepasiekiama"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentifikavimas atšauktas"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Neatpažinta"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Veidas neatpažintas"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autentifikavimas atšauktas"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nenustatytas PIN kodas, atrakinimo piešinys arba slaptažodis"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Autentifikuojant įvyko klaida"</string>
@@ -1852,7 +1853,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Jei norite išeiti, perbraukite žemyn iš viršaus."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Supratau"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Pasukite, kad geriau matytumėte vaizdą"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Išeikite iš išskaidyto ekrano režimo, kad geriau matytumėte vaizdą"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Atidarykite „<xliff:g id="NAME">%s</xliff:g>“ viso ekrano režimu, kad geriau matytumėte vaizdą"</string>
<string name="done_label" msgid="7283767013231718521">"Atlikta"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Apskritas valandų šliaužiklis"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Apskritas minučių šliaužiklis"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 0c0082967dff..d874fddf5d5a 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -621,6 +621,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrisko datu aparatūra nav pieejama"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentifikācija ir atcelta"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Dati nav atpazīti"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Seja netika atpazīta"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autentifikācija ir atcelta"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"PIN, kombinācija vai parole nav iestatīta"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Autentifikācijas kļūda"</string>
@@ -1851,7 +1852,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Lai izietu, no augšdaļas velciet lejup."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Labi"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Lai uzlabotu skatu, pagrieziet ekrānu."</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Lai uzlabotu skatu, izejiet no ekrāna sadalīšanas režīma."</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Labākam skatam atveriet lietotni <xliff:g id="NAME">%s</xliff:g> pilnekrāna režīmā."</string>
<string name="done_label" msgid="7283767013231718521">"Gatavs"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Stundu apļveida slīdnis"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Minūšu apļveida slīdnis"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 0a4c9bf30243..b0df176c3a0a 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометрискиот хардвер е недостапен"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Проверката е откажана"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Непознат"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Ликот не е препознаен"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Проверката е откажана"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Не е поставен PIN, шема или лозинка"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Грешка при проверката"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"За да излезете, повлечете одозгора надолу."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Сфатив"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Ротирајте за подобар приказ"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"За подобар приказ, излезете од поделениот екран"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"За подобар приказ, отворете ја апликацијата <xliff:g id="NAME">%s</xliff:g> на цел екран"</string>
<string name="done_label" msgid="7283767013231718521">"Готово"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Приказ на часови во кружно движење"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Приказ на минути во кружно движење"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 5fa3b52d7000..efbb0e13e890 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ബയോമെട്രിക് ഹാർ‌ഡ്‌വെയർ ലഭ്യമല്ല"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"പരിശോധിച്ചുറപ്പിക്കൽ റദ്ദാക്കി"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"തിരിച്ചറിഞ്ഞില്ല"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"മുഖം തിരിച്ചറിഞ്ഞിട്ടില്ല"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"പരിശോധിച്ചുറപ്പിക്കൽ റദ്ദാക്കി"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"പിന്നോ പാറ്റേണോ പാസ്‌വേഡോ സജ്ജീകരിച്ചിട്ടില്ല"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"പിശക് പരിശോധിച്ചുറപ്പിക്കുന്നു"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"അവസാനിപ്പിക്കാൻ, മുകളിൽ നിന്ന് താഴോട്ട് സ്വൈപ്പ് ചെയ്യുക."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"മനസ്സിലായി"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"മികച്ച കാഴ്‌ചയ്‌ക്കായി റൊട്ടേറ്റ് ചെയ്യുക"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"മികച്ച കാഴ്‌ചയ്‌ക്കായി സ്‌ക്രീൻ വിഭജന മോഡിൽ നിന്ന് പുറത്തുകടക്കുക"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"മികച്ച കാഴ്‌‌ചയ്ക്ക് പൂർണ്ണ സ്‌ക്രീനിൽ <xliff:g id="NAME">%s</xliff:g> തുറക്കുക"</string>
<string name="done_label" msgid="7283767013231718521">"പൂർത്തിയാക്കി"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"ചാക്രികമായി മണിക്കൂറുകൾ ദൃശ്യമാകുന്ന സ്ലൈഡർ"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"ചാക്രികമായി മിനിറ്റുകൾ ദൃശ്യമാകുന്ന സ്ലൈഡർ"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 204cf39de5f6..8df63ece2264 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометрийн техник хангамж боломжгүй байна"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Нотолгоог цуцаллаа"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Таниагүй"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Царайг таньсангүй"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Нотолгоог цуцаллаа"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Тохируулсан пин, хээ эсвэл нууц үг алга"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Баталгаажуулахад алдаа гарлаа"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Гарахаар бол дээрээс нь доош нь чирнэ үү."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Ойлголоо"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Харагдах байдлыг сайжруулах бол эргүүлнэ үү"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Харагдах байдлыг сайжруулах бол дэлгэцийг хуваах горимоос гарна уу"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Харагдах байдлыг сайжруулах бол <xliff:g id="NAME">%s</xliff:g>-г бүтэн дэлгэцээр нээнэ үү"</string>
<string name="done_label" msgid="7283767013231718521">"Дууссан"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Цаг гүйлгэгч"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Минут гүйлгэгч"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index c728050bfa2b..432ffce831b2 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"बायोमेट्रिक हार्डवेअर उपलब्ध नाही"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"ऑथेंटिकेशन रद्द केले"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"ओळखले नाही"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"चेहरा ओळखता आला नाही"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"ऑथेंटिकेशन रद्द केले"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"कोणताही पिन, पॅटर्न किंवा पासवर्ड सेट केलेला नाही"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"एरर ऑथेंटिकेट करत आहे"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"बाहेर पडण्यासाठी, वरून खाली स्वाइप करा."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"समजले"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"अधिक चांगल्या दृश्यासाठी फिरवा"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"अधिक चांगल्या दृश्यासाठी स्प्लिट स्क्रीनमधून बाहेर पडा"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"अधिक चांगल्या दृश्यासाठी <xliff:g id="NAME">%s</xliff:g> हे फुल स्क्रीनमध्ये उघडा"</string>
<string name="done_label" msgid="7283767013231718521">"पूर्ण झाले"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"तास परिपत्रक स्लायडर"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"मिनिटे परिपत्रक स्लायडर"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 5cb9c09890fe..ec737c916c92 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Perkakasan biometrik tidak tersedia"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Pengesahan dibatalkan"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Tidak dikenali"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Wajah tidak dikenali"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Pengesahan dibatalkan"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Pin, corak atau kata laluan tidak ditetapkan"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Ralat semasa membuat pengesahan"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Untuk keluar, leret dari atas ke bawah."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Faham"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Putar untuk mendapatkan paparan yang lebih baik"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Keluar daripada skrin pisah untuk mendapatkan paparan yang lebih baik"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Buka <xliff:g id="NAME">%s</xliff:g> dalam skrin penuh untuk mendapatkan paparan yang lebih baik"</string>
<string name="done_label" msgid="7283767013231718521">"Selesai"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Penggelangsar bulatan jam"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Penggelangsar bulatan minit"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index d90c5b825537..2df17b48f605 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -199,7 +199,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"အာရုံခံကိရိယာ အကြောင်းကြားချက် ဝန်ဆောင်မှု"</string>
<string name="twilight_service" msgid="8964898045693187224">"နေဝင်ဆည်းဆာ ဝန်ဆောင်မှု"</string>
<string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS အချိန်အပ်ဒိတ် ဝန်ဆောင်မှု"</string>
- <string name="device_policy_manager_service" msgid="5085762851388850332">"ကိရိယာဆိုင်ရာ မူဝါဒ မန်နေဂျာဝန်ဆောင်မှု"</string>
+ <string name="device_policy_manager_service" msgid="5085762851388850332">"ကိရိယာ မူဝါဒ မန်နေဂျာဝန်ဆောင်မှု"</string>
<string name="music_recognition_manager_service" msgid="7481956037950276359">"တေးဂီတကို သိရှိမှတ်မိခြင်း စီမံခန့်ခွဲမှုစနစ် ဝန်ဆောင်မှု"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"သင့်ကိရိယာအား ပယ်ဖျက်လိမ့်မည်"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"စက်စီမံအက်ပ်ကို သုံး၍မရပါ။ သင်၏ စက်ပစ္စည်းအတွင်းရှိ အရာများကို ဖျက်လိုက်ပါမည်\n\nမေးစရာများရှိပါက သင့်အဖွဲ့အစည်း၏ စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။"</string>
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ဇီဝအချက်အလက်သုံး ကွန်ပျူတာစက်ပစ္စည်း မရရှိနိုင်ပါ"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"အထောက်အထားစိစစ်ခြင်းကို ပယ်ဖျက်လိုက်သည်"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"မသိ"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"မျက်နှာကို မသိရှိပါ"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"အထောက်အထားစိစစ်ခြင်းကို ပယ်ဖျက်လိုက်သည်"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"ပင်နံပါတ်၊ လော့ခ်ပုံစံ သို့မဟုတ် စကားဝှက် သတ်မှတ်မထားပါ"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"အထောက်အထားစိစစ်ရာတွင် အမှားအယွင်းရှိနေသည်"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"ထွက်ရန် အပေါ်မှ အောက်သို့ ဆွဲချပါ။"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"ရပါပြီ"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ပိုကောင်းသောမြင်ကွင်းအတွက် လှည့်ပါ"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"ပိုကောင်းသောမြင်ကွင်းအတွက် မျက်နှာပြင် ခွဲ၍ပြသခြင်းမှ ထွက်ပါ"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"ပိုကောင်းသောမြင်ကွင်းအတွက် ဖန်သားပြင်အပြည့်ဖြင့် <xliff:g id="NAME">%s</xliff:g> ကို ဖွင့်ပါ"</string>
<string name="done_label" msgid="7283767013231718521">"ပြီးပါပြီ"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"နာရီရွေးချက်စရာ"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"မိနစ်လှည့်သော ရွေ့လျားတန်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 845eb92dc559..f8c8aaad345b 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrisk maskinvare er utilgjengelig"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentiseringen er avbrutt"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Ikke gjenkjent"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Gjenkjenner ikke ansiktet"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autentiseringen er avbrutt"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"PIN-kode, mønster eller passord er ikke angitt"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Feil under autentiseringen"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Sveip ned fra toppen for å avslutte."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Skjønner"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Roter for å få en bedre visning"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Avslutt delt skjerm for å få en bedre visning"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Åpne <xliff:g id="NAME">%s</xliff:g> i fullskjerm for å se bedre"</string>
<string name="done_label" msgid="7283767013231718521">"Ferdig"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Sirkulær glidebryter for timer"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Sirkulær glidebryter for minutter"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index e7de30871a0d..dd0db08ffa6e 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -89,8 +89,8 @@
<string name="notification_channel_call_forward" msgid="8230490317314272406">"कल फर्वार्ड गर्ने सेवा"</string>
<string name="notification_channel_emergency_callback" msgid="54074839059123159">"आपत्‌कालीन कलब्याक मोड"</string>
<string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"मोबाइल डेटाको स्थिति"</string>
- <string name="notification_channel_sms" msgid="1243384981025535724">"SMS सन्देशहरू"</string>
- <string name="notification_channel_voice_mail" msgid="8457433203106654172">"भ्वाइस मेल सन्देशहरू"</string>
+ <string name="notification_channel_sms" msgid="1243384981025535724">"SMS म्यासेजहरू"</string>
+ <string name="notification_channel_voice_mail" msgid="8457433203106654172">"भ्वाइस मेल म्यासेजहरू"</string>
<string name="notification_channel_wfc" msgid="9048240466765169038">"Wi-Fi कल"</string>
<string name="notification_channel_sim" msgid="5098802350325677490">"SIM को स्थिति"</string>
<string name="notification_channel_sim_high_prio" msgid="642361929452850928">"उच्च प्राथमिकता रहेको SIM को स्थिति"</string>
@@ -122,7 +122,7 @@
<string name="roamingTextSearching" msgid="5323235489657753486">"सेवाको खोजी गर्दै…"</string>
<string name="wfcRegErrorTitle" msgid="3193072971584858020">"Wi-Fi कलिङ सेटअप गर्न सकिएन"</string>
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="468830943567116703">"Wi-Fi मार्फत कलहरू गर्न र सन्देशहरू पठाउन सबभन्दा पहिला आफ्नो सेवा प्रदायकलाई यो सेवा सेट गर्न भन्नुहोस्। त्यसपछि सेटिङहरूबाट Wi-Fi कलिङलाई सक्रिय पार्नुहोस्। (त्रुटिसम्बन्धी कोड: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
+ <item msgid="468830943567116703">"Wi-Fi मार्फत कलहरू गर्न र म्यासेजहरू पठाउन सबभन्दा पहिला आफ्नो सेवा प्रदायकलाई यो सेवा सेट गर्न भन्नुहोस्। त्यसपछि सेटिङहरूबाट Wi-Fi कलिङलाई सक्रिय पार्नुहोस्। (त्रुटिसम्बन्धी कोड: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="4795145070505729156">"तपाईंको सेवा प्रदायकमार्फत Wi-Fi कलिङ सुविधा दर्ता गर्ने क्रममा देखिएको समस्या: <xliff:g id="CODE">%1$s</xliff:g>"</item>
@@ -275,8 +275,8 @@
<string name="notification_channel_security" msgid="8516754650348238057">"सुरक्षा"</string>
<string name="notification_channel_car_mode" msgid="2123919247040988436">"कार मोड"</string>
<string name="notification_channel_account" msgid="6436294521740148173">"खाताको स्थिति"</string>
- <string name="notification_channel_developer" msgid="1691059964407549150">"विकासकर्ताका सन्देशहरू"</string>
- <string name="notification_channel_developer_important" msgid="7197281908918789589">"विकासकर्तासम्बन्धी महत्त्वपूर्ण सन्देशहरू"</string>
+ <string name="notification_channel_developer" msgid="1691059964407549150">"विकासकर्ताका म्यासेजहरू"</string>
+ <string name="notification_channel_developer_important" msgid="7197281908918789589">"विकासकर्तासम्बन्धी महत्त्वपूर्ण म्यासेजहरू"</string>
<string name="notification_channel_updates" msgid="7907863984825495278">"अद्यावधिकहरू"</string>
<string name="notification_channel_network_status" msgid="2127687368725272809">"नेटवर्कको स्थिति"</string>
<string name="notification_channel_network_alerts" msgid="6312366315654526528">"नेटवर्कका अलर्टहरू"</string>
@@ -300,14 +300,14 @@
<string name="managed_profile_label" msgid="7316778766973512382">"कार्य प्रोफाइलमा बदल्नुहोस्"</string>
<string name="user_owner_app_label" msgid="1553595155465750298">"<xliff:g id="APP_NAME">%1$s</xliff:g> को व्यक्तिगत प्रोफाइल प्रयोग गर्नुहोस्"</string>
<string name="managed_profile_app_label" msgid="367401088383965725">"<xliff:g id="APP_NAME">%1$s</xliff:g> को कार्य प्रोफाइल प्रयोग गर्नुहोस्"</string>
- <string name="permgrouplab_contacts" msgid="4254143639307316920">"सम्पर्कहरू"</string>
+ <string name="permgrouplab_contacts" msgid="4254143639307316920">"कन्ट्याक्टहरू"</string>
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"तपाईँको सम्पर्कमाथि पहुँच गर्नुहोस्"</string>
<string name="permgrouplab_location" msgid="1858277002233964394">"लोकेसन"</string>
<string name="permgroupdesc_location" msgid="1995955142118450685">"यस डिभाइसको स्थानमाथि पहुँच"</string>
<string name="permgrouplab_calendar" msgid="6426860926123033230">"पात्रो"</string>
<string name="permgroupdesc_calendar" msgid="6762751063361489379">"तपाईंको पात्रोमाथि पहुँच गर्नुहोस्"</string>
<string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
- <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS सन्देशहरू पठाउनुहोस् र हेर्नुहोस्"</string>
+ <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS म्यासेजहरू पठाउनुहोस् र हेर्नुहोस्"</string>
<string name="permgrouplab_storage" msgid="17339216290379241">"फाइलहरू"</string>
<string name="permgroupdesc_storage" msgid="5378659041354582769">"आफ्नो डिभाइसमा रहेका फाइलहरू हेर्नुहोस् र प्रयोग गर्नुहोस्"</string>
<string name="permgrouplab_readMediaAural" msgid="1858331312624942053">"सङ्गीत तथा अडियो"</string>
@@ -362,25 +362,25 @@
<string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"फोन कलहरूको जवाफ दिनुहोस्"</string>
<string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"एपलाई आगमन फोन कलको जवाफ दिन अनुमति दिन्छ।"</string>
<string name="permlab_receiveSms" msgid="505961632050451881">"टेक्स्ट म्यासेजहरू (SMS) प्राप्त गर्नुहोस्"</string>
- <string name="permdesc_receiveSms" msgid="1797345626687832285">"एपलाई SMS सन्देशहरू प्राप्त गर्न र प्रक्रिया गर्न अनुमति दिन्छ। यसको मतलब अनुप्रयोगले तपाईंको उपकरणमा पठाइएको सन्देशहरू तपाईंलाई नदेखाईनै मोनिटर गर्न वा मेटाउन सक्दछ।"</string>
+ <string name="permdesc_receiveSms" msgid="1797345626687832285">"एपलाई SMS म्यासेजहरू प्राप्त गर्न र प्रक्रिया गर्न अनुमति दिन्छ। यसको मतलब अनुप्रयोगले तपाईंको उपकरणमा पठाइएको म्यासेजहरू तपाईंलाई नदेखाईनै मोनिटर गर्न वा मेटाउन सक्दछ।"</string>
<string name="permlab_receiveMms" msgid="4000650116674380275">"टेक्स्ट म्यासेज (MMS) प्राप्त गर्नुहोस्"</string>
- <string name="permdesc_receiveMms" msgid="958102423732219710">"एपलाई MMS सन्देशहरू प्राप्त गर्न र प्रकृया गर्न अनुमति दिन्छ। यसको मतलब अनुप्रयोगले तपाईंको उपकरणमा पठाइएको सन्देशहरू तपाईंलाई नदेखाईनै मोनिटर गर्न वा मेटाउन सक्दछ।"</string>
- <string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"मोबाइल प्रसारणसम्बन्धी सन्देशहरू फर्वार्ड गर्नुहोस्"</string>
- <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"मोबाइल प्रसारणसम्बन्धी सन्देशहरू प्राप्त हुनासाथै तिनीहरूलाई फर्वार्ड गर्नका लागि यसले एपलाई मोबाइल प्रसारण मोड्युलमा जोडिने अनुमति दिन्छ। तपाईंलाई कतिपय स्थानमा आपत्‌कालीन अवस्थाका बारेमा जानकारी दिनका लागि मोबाइल प्रसारणसम्बन्धी अलर्टहरू पठाइन्छ। हानिकारक एपहरूले आपत्‌कालीन मोबाइल प्रसारण प्राप्त हुँदा तपाईंको यन्त्रलाई कार्य सम्पादन गर्ने वा सञ्चालित हुने क्रममा हस्तक्षेप गर्न सक्छन्।"</string>
+ <string name="permdesc_receiveMms" msgid="958102423732219710">"एपलाई MMS म्यासेजहरू प्राप्त गर्न र प्रकृया गर्न अनुमति दिन्छ। यसको मतलब अनुप्रयोगले तपाईंको उपकरणमा पठाइएको म्यासेजहरू तपाईंलाई नदेखाईनै मोनिटर गर्न वा मेटाउन सक्दछ।"</string>
+ <string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"मोबाइल प्रसारणसम्बन्धी म्यासेजहरू फर्वार्ड गर्नुहोस्"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"मोबाइल प्रसारणसम्बन्धी म्यासेजहरू प्राप्त हुनासाथै तिनीहरूलाई फर्वार्ड गर्नका लागि यसले एपलाई मोबाइल प्रसारण मोड्युलमा जोडिने अनुमति दिन्छ। तपाईंलाई कतिपय स्थानमा आपत्‌कालीन अवस्थाका बारेमा जानकारी दिनका लागि मोबाइल प्रसारणसम्बन्धी अलर्टहरू पठाइन्छ। हानिकारक एपहरूले आपत्‌कालीन मोबाइल प्रसारण प्राप्त हुँदा तपाईंको यन्त्रलाई कार्य सम्पादन गर्ने वा सञ्चालित हुने क्रममा हस्तक्षेप गर्न सक्छन्।"</string>
<string name="permlab_manageOngoingCalls" msgid="281244770664231782">"जारी रहेका कलहरू व्यवस्थापन गर्न"</string>
<string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"तपाईं यो एपलाई अनुमति दिनुभयो यस एपले तपाईंको डिभाइसमा जारी रहेका कलसम्बन्धी विवरण हेर्न र ती कलहरू नियन्त्रण गर्न सक्छ।"</string>
- <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"सेल प्रसारित सन्देशहरू पढ्नुहोस्"</string>
- <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"तपाईंको उपकरणद्वारा प्राप्त सेल प्रसारण सन्देशहरू एपलाई पढ्न अनुमति दिन्छ। सेल प्रसारण चेतावनीहरू केही स्थानहरूमा तपाईंलाई आपत्‌कालीन गतिविधिहरूको बारेमा सचेत गराउन गरिएका छन्। खराब एपहरूले एउटा आपत्‌कालीन सेल प्रसारण प्राप्त गर्दछ जब तपाईंको उपकरणको प्रदर्शन वा अपरेशनको साथ हस्तक्षेप गर्न सक्दछन्।"</string>
+ <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"सेल प्रसारित म्यासेजहरू पढ्नुहोस्"</string>
+ <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"तपाईंको उपकरणद्वारा प्राप्त सेल प्रसारण म्यासेजहरू एपलाई पढ्न अनुमति दिन्छ। सेल प्रसारण चेतावनीहरू केही स्थानहरूमा तपाईंलाई आपत्‌कालीन गतिविधिहरूको बारेमा सचेत गराउन गरिएका छन्। खराब एपहरूले एउटा आपत्‌कालीन सेल प्रसारण प्राप्त गर्दछ जब तपाईंको उपकरणको प्रदर्शन वा अपरेशनको साथ हस्तक्षेप गर्न सक्दछन्।"</string>
<string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"सदस्य बनाइका फिडहरू पढ्नुहोस्"</string>
<string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"एपलाई अहिलेको समीकरण गरिएका सूचकहरू बारे विवरणहरू लिने अनुमति दिन्छ।"</string>
- <string name="permlab_sendSms" msgid="7757368721742014252">"SMS सन्देशहरू पठाउनुहोस् र हेर्नुहोस्"</string>
- <string name="permdesc_sendSms" msgid="6757089798435130769">"एपलाई SMS सन्देशहरू पठाउन अनुमति दिन्छ। यसले अप्रत्यासित चार्जहरूको परिणाम दिन सक्दछ। खराब एपहरूले तपाईंको पुष्टि बिना सन्देशहरू पठाएर तपाईंको पैसा खर्च गराउन सक्दछ।"</string>
+ <string name="permlab_sendSms" msgid="7757368721742014252">"SMS म्यासेजहरू पठाउनुहोस् र हेर्नुहोस्"</string>
+ <string name="permdesc_sendSms" msgid="6757089798435130769">"एपलाई SMS म्यासेजहरू पठाउन अनुमति दिन्छ। यसले अप्रत्यासित चार्जहरूको परिणाम दिन सक्दछ। खराब एपहरूले तपाईंको पुष्टि बिना म्यासेजहरू पठाएर तपाईंको पैसा खर्च गराउन सक्दछ।"</string>
<string name="permlab_readSms" msgid="5164176626258800297">"तपाईंका टेक्स्ट म्यासेजहरू (SMS वा MMS) पढ्नुहोस्"</string>
- <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"यस एपले तपाईंको ट्याब्लेटमा भण्डारण गरिएका सबै SMS (पाठ) सन्देशहरू पढ्न सक्छ।"</string>
- <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"यस एपले तपाईंको Android टिभी डिभाइसमा भण्डारण गरिएका सबै SMS.(पाठ) सन्देशहरू पढ्न सक्छ।"</string>
- <string name="permdesc_readSms" product="default" msgid="774753371111699782">"यस एपले तपाईंको फोनमा भण्डारण गरिएका सबै SMS (पाठ) सन्देशहरू पढ्न सक्छ।"</string>
+ <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"यस एपले तपाईंको ट्याब्लेटमा भण्डारण गरिएका सबै SMS (पाठ) म्यासेजहरू पढ्न सक्छ।"</string>
+ <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"यस एपले तपाईंको Android टिभी डिभाइसमा भण्डारण गरिएका सबै SMS.(पाठ) म्यासेजहरू पढ्न सक्छ।"</string>
+ <string name="permdesc_readSms" product="default" msgid="774753371111699782">"यस एपले तपाईंको फोनमा भण्डारण गरिएका सबै SMS (पाठ) म्यासेजहरू पढ्न सक्छ।"</string>
<string name="permlab_receiveWapPush" msgid="4223747702856929056">"टेक्स्ट म्यासेजहरू (WAP) प्राप्त गर्नुहोस्"</string>
- <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"WAP सन्देशहरू प्राप्त गर्न र प्रशोधन गर्न एपलाई अनुमति दिन्छ। यो अनुमतिमा मोनिटर गर्ने वा तपाईँलाई पठाइएका म्यासेजहरू तपाईँलाई नदेखाई मेट्ने क्षमता समावेश हुन्छ।"</string>
+ <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"WAP म्यासेजहरू प्राप्त गर्न र प्रशोधन गर्न एपलाई अनुमति दिन्छ। यो अनुमतिमा मोनिटर गर्ने वा तपाईँलाई पठाइएका म्यासेजहरू तपाईँलाई नदेखाई मेट्ने क्षमता समावेश हुन्छ।"</string>
<string name="permlab_getTasks" msgid="7460048811831750262">"चलिरहेका एपहरू पुनःबहाली गर्नुहोस्"</string>
<string name="permdesc_getTasks" msgid="7388138607018233726">"वर्तमानमा र भरखरै चलिरहेका कार्यहरू बारेको सूचना पुनःबहाली गर्न एपलाई अनुमित दिन्छ। यसले उपकरणमा प्रयोग भएका अनुप्रयोगहरूको बारेमा सूचना पत्ता लगाउन एपलाई अनुमति दिन सक्छ।"</string>
<string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"प्रोफाइल र यन्त्र मालिकहरूको व्यवस्थापन गराउनुहोस्"</string>
@@ -470,9 +470,9 @@
<string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"यस एपले तपाईंको Android टिभी डिभाइसमा भण्डारण गरिएका पात्रोसम्बन्धी सबै कार्यक्रमहरू पढ्न र तपाईंको पात्रोको डेटा आदान प्रदान वा सुरक्षित गर्न सक्छ।"</string>
<string name="permdesc_readCalendar" product="default" msgid="9118823807655829957">"यस एपले तपाईंको फोनमा भण्डारण गरिएका पात्रो सम्बन्धी सबै कार्यक्रमहरू पढ्न र तपाईंको पात्रोको डेटा आदान प्रदान वा सुरक्षित गर्न सक्छ।"</string>
<string name="permlab_writeCalendar" msgid="6422137308329578076">"पात्रो घटनाहरू थप्नुहोस् वा परिमार्जन गर्नुहोस् र मालिकको ज्ञान बिना नै पाहुनाहरूलाई इमेल पठाउनुहोस्"</string>
- <string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"यस एपले तपाईंको ट्याब्लेटमा पात्रोका कार्यक्रमहरू थप्न, हटाउन वा परिवर्तन गर्न सक्छ। यस एपले पात्रोका मालिकहरू मार्फत आएको जस्तो लाग्ने सन्देशहरू पठाउन वा तिनीहरूका मालिकहरूलाई सूचित नगरिकन कार्यक्रमहरू परिवर्तन गर्न सक्छ।"</string>
- <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"यस एपले तपाईंको Android टिभी डिभाइसमा पात्रोका कार्यक्रमहरू थप्न, हटाउन वा परिवर्तन गर्न सक्छ। यस एपले पात्रोका मालिकहरूले पठाएको जस्तै देखिने सन्देशहरू पठाउन वा कार्यक्रमका मालिकहरूलाई सूचित नगरिकन कार्यक्रमहरू परिवर्तन गर्न सक्छ।"</string>
- <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"यस एपले तपाईंको फोनमा पात्रोका कार्यक्रमहरू थप्न, हटाउन वा परिवर्तन गर्न सक्छ। यस एपले पात्रोका मालिकहरू मार्फत आएको जस्तो लाग्ने सन्देशहरू पठाउन वा तिनीहरूका मालिकहरूलाई सूचित नगरिकन कार्यक्रमहरू परिवर्तन गर्न सक्छ।"</string>
+ <string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"यस एपले तपाईंको ट्याब्लेटमा पात्रोका कार्यक्रमहरू थप्न, हटाउन वा परिवर्तन गर्न सक्छ। यस एपले पात्रोका मालिकहरू मार्फत आएको जस्तो लाग्ने म्यासेजहरू पठाउन वा तिनीहरूका मालिकहरूलाई सूचित नगरिकन कार्यक्रमहरू परिवर्तन गर्न सक्छ।"</string>
+ <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"यस एपले तपाईंको Android टिभी डिभाइसमा पात्रोका कार्यक्रमहरू थप्न, हटाउन वा परिवर्तन गर्न सक्छ। यस एपले पात्रोका मालिकहरूले पठाएको जस्तै देखिने म्यासेजहरू पठाउन वा कार्यक्रमका मालिकहरूलाई सूचित नगरिकन कार्यक्रमहरू परिवर्तन गर्न सक्छ।"</string>
+ <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"यस एपले तपाईंको फोनमा पात्रोका कार्यक्रमहरू थप्न, हटाउन वा परिवर्तन गर्न सक्छ। यस एपले पात्रोका मालिकहरू मार्फत आएको जस्तो लाग्ने म्यासेजहरू पठाउन वा तिनीहरूका मालिकहरूलाई सूचित नगरिकन कार्यक्रमहरू परिवर्तन गर्न सक्छ।"</string>
<string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"अधिक स्थान प्रदायक आदेशहरू पहुँच गर्नुहोस्"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"एपलाई अतिरिक्त स्थान प्रदायक आदेशहरू पहुँच गर्न अनुमति दिन्छ। यो एपलाई GPS वा अन्य स्थान स्रोतहरूको संचालन साथै हस्तक्षेप गर्न अनुमति दिन सक्छ।"</string>
<string name="permlab_accessFineLocation" msgid="6426318438195622966">"अग्रभूमिमा मात्र सटीक स्थानमाथि पहुँच राख्नुहोस्"</string>
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"बायोमेट्रिक हार्डवेयर उपलब्ध छैन"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"प्रमाणीकरण रद्द गरियो"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"पहिचान भएन"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"अनुहार पहिचान गर्न सकिएन"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"प्रमाणीकरण रद्द गरियो"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"कुनै पनि PIN, ढाँचा वा पासवर्ड सेट गरिएको छैन"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"प्रमाणित गर्ने क्रममा त्रुटि भयो"</string>
@@ -1143,7 +1144,7 @@
<string name="selectAll" msgid="1532369154488982046">"सबैलाई चयन गर्नुहोस्"</string>
<string name="cut" msgid="2561199725874745819">"काट्नुहोस्"</string>
<string name="copy" msgid="5472512047143665218">"कपी गर्नुहोस्"</string>
- <string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"क्लिपबोर्डमा प्रतिलिपि गर्न सकिएन"</string>
+ <string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"क्लिपबोर्डमा कपी गर्न सकिएन"</string>
<string name="paste" msgid="461843306215520225">"टाँस्नुहोस्"</string>
<string name="paste_as_plain_text" msgid="7664800665823182587">"सामान्य पाठको रूपमा टाँस्नुहोस्"</string>
<string name="replace" msgid="7842675434546657444">"विस्थापन गर्नुहोस्…"</string>
@@ -1323,8 +1324,8 @@
<string name="accept" msgid="5447154347815825107">"स्वीकार्नुहोस्"</string>
<string name="decline" msgid="6490507610282145874">"अस्वीकार गर्नुहोस्"</string>
<string name="select_character" msgid="3352797107930786979">"अक्षरहरू प्रवेश गराउनुहोस्"</string>
- <string name="sms_control_title" msgid="4748684259903148341">"SMS सन्देशहरू पठाइँदै"</string>
- <string name="sms_control_message" msgid="6574313876316388239">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ले धरै संख्यामा SMS सन्देशहरू पठाउँदैछ। के तपाईं यस एपलाई सन्देशहरू पठाउन सुचारु गर्न अनुमति दिन चाहनु हुन्छ?"</string>
+ <string name="sms_control_title" msgid="4748684259903148341">"SMS म्यासेजहरू पठाइँदै"</string>
+ <string name="sms_control_message" msgid="6574313876316388239">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ले धरै संख्यामा SMS म्यासेजहरू पठाउँदैछ। के तपाईं यस एपलाई म्यासेजहरू पठाउन सुचारु गर्न अनुमति दिन चाहनु हुन्छ?"</string>
<string name="sms_control_yes" msgid="4858845109269524622">"अनुमति दिनुहोस्"</string>
<string name="sms_control_no" msgid="4845717880040355570">"अस्वीकार गर्नुहोस्"</string>
<string name="sms_short_code_confirm_message" msgid="1385416688897538724">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; के तपाईं सन्देश पठाउन चाहुनु हुन्छ &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt;."</string>
@@ -1513,7 +1514,7 @@
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ड्राइभिङ अनुप्रयोगबाट बाहिर निस्कन ट्याप गर्नुहोस्।"</string>
<string name="back_button_label" msgid="4078224038025043387">"पछाडि"</string>
<string name="next_button_label" msgid="6040209156399907780">"अर्को"</string>
- <string name="skip_button_label" msgid="3566599811326688389">"छोड्नुहोस्"</string>
+ <string name="skip_button_label" msgid="3566599811326688389">"स्किप गर्नुहोस्"</string>
<string name="no_matches" msgid="6472699895759164599">"कुनै मिलेन"</string>
<string name="find_on_page" msgid="5400537367077438198">"पृष्ठमा फेला पार्नुहोस्"</string>
<string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# वटा मिल्दोजुल्दो परिणाम}other{{total} मध्ये # वटा मिल्दाजुल्दा परिणाम}}"</string>
@@ -1650,8 +1651,8 @@
<string name="kg_sim_pin_instructions" msgid="6479401489471690359">"SIM PIN हाल्नुहोस्"</string>
<string name="kg_pin_instructions" msgid="7355933174673539021">"PIN हाल्नुहोस्"</string>
<string name="kg_password_instructions" msgid="7179782578809398050">"पासवर्ड प्रविष्टि गर्नुहोस्"</string>
- <string name="kg_puk_enter_puk_hint" msgid="6696187482616360994">"SIM कार्ड अहिले असक्षम छ। सुचारु गर्नको लागि PUK कोड प्रविष्टि गर्नुहोस्। विवरणको लागि वाहकलाई सम्पर्क गर्नुहोस्।"</string>
- <string name="kg_puk_enter_pin_hint" msgid="8190982314659429770">"इच्छित PIN कोड प्रविष्टि गर्नुहोस्"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="6696187482616360994">"SIM कार्ड अहिले असक्षम छ। सुचारु गर्नको लागि PUK कोड हाल्नुहोस्। विवरणको लागि वाहकलाई सम्पर्क गर्नुहोस्।"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="8190982314659429770">"इच्छित PIN कोड हाल्नुहोस्"</string>
<string name="kg_enter_confirm_pin_hint" msgid="6372557107414074580">"मनपर्दो PIN कोड निश्चित गर्नुहोस्"</string>
<string name="kg_sim_unlock_progress_dialog_message" msgid="5743634657721110967">"SIM कार्ड अनलक गरिँदै छ…"</string>
<string name="kg_password_wrong_pin_code" msgid="9013856346870572451">"गलत PIN कोड।"</string>
@@ -1715,7 +1716,7 @@
<string name="color_correction_feature_name" msgid="7975133554160979214">"रङ सच्याउने सुविधा"</string>
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"एक हाते मोड"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"अझै मधुरो"</string>
- <string name="hearing_aids_feature_name" msgid="1125892105105852542">"हियरिङ डिभाइसहरू"</string>
+ <string name="hearing_aids_feature_name" msgid="1125892105105852542">"श्रवण यन्त्रहरू"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अन भयो।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अफ भयो।"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"भोल्युम बटनहरू थिच्न छाड्नुहोस्। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अन गर्न दुवै भोल्युम बटन फेरि ३ सेकेन्डसम्म थिचिराख्नुहोस्।"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"बाहिर निस्कन, माथिबाट तल स्वाइप गर्नुहोस्।"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"बुझेँ"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"अझ राम्रो दृश्य हेर्न चाहनुहुन्छ भने रोटेट गर्नुहोस्"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"अझ राम्रो दृश्य हेर्न चाहनुहुन्छ भने \"स्प्लिट स्क्रिन\" बाट बाहिरिनुहोस्"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"अझ राम्रो दृश्य हेर्न चाहनुहुन्छ भने <xliff:g id="NAME">%s</xliff:g> खोल्नुहोस्"</string>
<string name="done_label" msgid="7283767013231718521">"भयो"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"घन्टा गोलाकार स्लाइडर"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"मिनेट गोलाकार स्लाइडर"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index f2b03086ea94..30b487b8d29a 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrische hardware niet beschikbaar"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Verificatie geannuleerd"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Niet herkend"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Gezicht niet herkend"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Verificatie geannuleerd"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Geen pincode, patroon of wachtwoord ingesteld"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Fout bij verificatie"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Swipe omlaag vanaf de bovenkant van het scherm om af te sluiten."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Ik snap het"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Draai voor een betere weergave"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Sluit het gesplitste scherm voor een betere weergave"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Open <xliff:g id="NAME">%s</xliff:g> op volledig scherm voor een betere weergave"</string>
<string name="done_label" msgid="7283767013231718521">"Klaar"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Ronde schuifregelaar voor uren"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Ronde schuifregelaar voor minuten"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 71e6606ab3d8..095830a48593 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ବାୟୋମେଟ୍ରିକ୍‌ ହାର୍ଡୱେର୍‌ ଉପଲବ୍ଧ ନାହିଁ"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"ପ୍ରାମାଣିକତାକୁ ବାତିଲ୍ କରାଯାଇଛି"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"ଚିହ୍ନଟ ହେଲାନାହିଁ"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"ଫେସ ଚିହ୍ନଟ କରାଯାଇନାହିଁ"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"ପ୍ରାମାଣିକତାକୁ ବାତିଲ୍ କରାଯାଇଛି"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"କୌଣସି ପିନ୍, ପେଟେର୍ନ ବା ପାସ୍‍ୱର୍ଡ ସେଟ୍ ନାହିଁ"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"ପ୍ରାମାଣିକରଣ କରିବା ସମୟରେ ତ୍ରୁଟି"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"ବାହାରିବା ପାଇଁ, ଉପରୁ ତଳକୁ ସ୍ୱାଇପ୍‍ କରନ୍ତୁ।"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"ବୁଝିଗଲି"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ଏକ ଭଲ ଭ୍ୟୁ ପାଇଁ ରୋଟେଟ କରନ୍ତୁ"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"ଏକ ଭଲ ଭ୍ୟୁ ପାଇଁ ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନରୁ ବାହାରି ଯାଆନ୍ତୁ"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"ଏକ ଭଲ ଭ୍ୟୁ ପାଇଁ ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନରେ <xliff:g id="NAME">%s</xliff:g> ଖୋଲନ୍ତୁ"</string>
<string name="done_label" msgid="7283767013231718521">"ହୋଇଗଲା"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"ଘଣ୍ଟା ସର୍କୁଲାର୍‍ ସ୍ଲାଇଡର୍‍"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"ମିନିଟ୍ସ ସର୍କୁଲାର୍‍ ସ୍ଲାଇଡର୍‍"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 25f7f7cf6976..92bb4916f494 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -191,7 +191,7 @@
<string name="network_logging_notification_title" msgid="554983187553845004">"ਡੀਵਾਈਸ ਪ੍ਰਬੰਧਨ ਅਧੀਨ ਹੈ"</string>
<string name="network_logging_notification_text" msgid="1327373071132562512">"ਤੁਹਾਡਾ ਸੰਗਠਨ ਇਸ ਡੀਵਾਈਸ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਦਾ ਹੈ ਅਤੇ ਨੈੱਟਵਰਕ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦਾ ਹੈ। ਵੇਰਵਿਆਂ ਲਈ ਟੈਪ ਕਰੋ।"</string>
<string name="location_changed_notification_title" msgid="3620158742816699316">"ਐਪਾਂ ਤੁਹਾਡੇ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਕਰ ਸਕਦੀਆਂ ਹਨ"</string>
- <string name="location_changed_notification_text" msgid="7158423339982706912">"ਹੋਰ ਜਾਣਨ ਲਈ ਆਪਣੇ ਆਈ.ਟੀ. ਪ੍ਰਸ਼ਾਸਕ ਨੂੰ ਸੰਪਰਕ ਕਰੋ"</string>
+ <string name="location_changed_notification_text" msgid="7158423339982706912">"ਹੋਰ ਜਾਣਨ ਲਈ ਆਪਣੇ ਆਈ.ਟੀ. ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ"</string>
<string name="geofencing_service" msgid="3826902410740315456">"ਭੂਗੋਲਿਕ-ਘੇਰੇ ਸੰਬੰਧੀ ਸੇਵਾ"</string>
<string name="country_detector" msgid="7023275114706088854">"ਦੇਸ਼ ਦਾ ਪਤਾ ਲਗਾਉਣ ਦੀ ਸੁਵਿਧਾ"</string>
<string name="location_service" msgid="2439187616018455546">"ਟਿਕਾਣਾ ਸੇਵਾ"</string>
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ਬਾਇਓਮੈਟ੍ਰਿਕ ਹਾਰਡਵੇਅਰ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"ਪ੍ਰਮਾਣੀਕਰਨ ਰੱਦ ਕੀਤਾ ਗਿਆ"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"ਚਿਹਰੇ ਦੀ ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"ਪ੍ਰਮਾਣੀਕਰਨ ਰੱਦ ਕੀਤਾ ਗਿਆ"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"ਕੋਈ ਪਿੰਨ, ਪੈਟਰਨ ਜਾਂ ਪਾਸਵਰਡ ਸੈੱਟ ਨਹੀਂ ਕੀਤਾ ਗਿਆ"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"ਗੜਬੜ ਨੂੰ ਪ੍ਰਮਾਣਿਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
@@ -635,7 +636,7 @@
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"ਬਹੁਤ ਜ਼ਿਆਦਾ ਚਮਕ"</string>
<string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"ਪਾਵਰ ਬਟਨ ਦਬਾਏ ਜਾਣ ਦਾ ਪਤਾ ਲੱਗਿਆ ਹੈ"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ਵਿਵਸਥਿਤ ਕਰਕੇ ਦੇਖੋ"</string>
- <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ਹਰ ਵਾਰ ਆਪਣੀ ਉਂਗਲ ਨੂੰ ਥੋੜ੍ਹਾ ਹਿਲਾਓ"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ਹਰ ਵਾਰ ਆਪਣੀ ਉਂਗਲੀ ਦੀ ਸਥਿਤੀ ਨੂੰ ਥੋੜਾ ਜਿਹਾ ਬਦਲੋ"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_error_not_match" msgid="4599441812893438961">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"ਬਾਹਰ ਜਾਣ ਲਈ, ਉਪਰੋਂ ਹੇਠਾਂ ਸਵਾਈਪ ਕਰੋ।"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"ਸਮਝ ਲਿਆ"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ਬਿਹਤਰ ਦ੍ਰਿਸ਼ ਅਨੁਭਵ ਲਈ ਘੁਮਾਓ"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"ਬਿਹਤਰ ਦ੍ਰਿਸ਼ ਅਨੁਭਵ ਲਈ ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਤੋਂ ਬਾਹਰ ਆਓ"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"ਬਿਹਤਰ ਦ੍ਰਿਸ਼ ਅਨੁਭਵ ਲਈ <xliff:g id="NAME">%s</xliff:g> ਨੂੰ ਪੂਰੀ-ਸਕ੍ਰੀਨ ਵਿੱਚ ਖੋਲ੍ਹੋ"</string>
<string name="done_label" msgid="7283767013231718521">"ਹੋ ਗਿਆ"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"ਘੰਟੇ ਸਰਕੁਲਰ ਸਲਾਈਡਰ"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"ਮਿੰਟ ਸਰਕੁਲਰ ਸਲਾਈਡਰ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 9871ccbccaa0..f72ff68499fa 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -622,6 +622,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Sprzęt biometryczny niedostępny"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Anulowano uwierzytelnianie"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nie rozpoznano"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Nie rozpoznano twarzy"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Anulowano uwierzytelnianie"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nie ustawiono kodu PIN, wzoru ani hasła"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Podczas uwierzytelniania wystąpił błąd"</string>
@@ -1852,7 +1853,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Aby wyjść, przesuń palcem z góry na dół."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Obróć, aby lepiej widzieć"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Zamknij podzielony ekran, aby lepiej widzieć"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Otwórz aplikację <xliff:g id="NAME">%s</xliff:g> na pełnym ekranie, aby lepiej widzieć"</string>
<string name="done_label" msgid="7283767013231718521">"Gotowe"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Kołowy suwak godzin"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Kołowy suwak minut"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 2ce7094055ed..8160544d0922 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -621,13 +621,14 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biométrico indisponível"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autenticação cancelada"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Não reconhecido"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Rosto não reconhecido"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autenticação cancelada"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nenhum PIN, padrão ou senha configurado"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Erro na autenticação"</string>
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Usar bloqueio de tela"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Insira seu bloqueio de tela para continuar"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Pressione o sensor com firmeza"</string>
- <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Não foi possível reconhecer a impressão digital. Tente de novo."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Impressão digital não reconhecida. Tente de novo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Limpe o sensor de impressão digital e tente novamente"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Limpe o sensor e tente novamente"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Pressione o sensor com firmeza"</string>
@@ -1851,7 +1852,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Para sair, deslize de cima para baixo."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Entendi"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Gire a tela para ter uma visualização melhor"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Saia da tela dividida para ter uma visualização melhor"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Abra o app <xliff:g id="NAME">%s</xliff:g> em tela cheia para ter uma melhor visualização"</string>
<string name="done_label" msgid="7283767013231718521">"Concluído"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Controle deslizante circular das horas"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Controle deslizante circular dos minutos"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index ea30d7c3e322..50531dbc51a9 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -621,13 +621,14 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biométrico indisponível."</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autenticação cancelada"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Não reconhecido."</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Rosto não reconhecido"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autenticação cancelada"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nenhum PIN, padrão ou palavra-passe definidos."</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Erro ao autenticar."</string>
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Usar o bloqueio de ecrã"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Introduza o bloqueio de ecrã para continuar"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Prima firmemente o sensor"</string>
- <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Não é possível reconhecer a impressão digital. Tente novamente."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Impossível reconhecer impressão digital. Volte a tentar."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Limpe o sensor de impressões digitais e tente novamente"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Limpe o sensor e tente novamente"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Prima firmemente o sensor"</string>
@@ -1851,7 +1852,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Para sair, deslize rapidamente para baixo a partir da parte superior."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rode para uma melhor visualização"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Saia do ecrã dividido para uma melhor visualização"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Abra <xliff:g id="NAME">%s</xliff:g> em ecrã inteiro para uma melhor visualização"</string>
<string name="done_label" msgid="7283767013231718521">"Concluído"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Controlo de deslize circular das horas"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Controlo de deslize circular dos minutos"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 2ce7094055ed..8160544d0922 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -621,13 +621,14 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biométrico indisponível"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autenticação cancelada"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Não reconhecido"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Rosto não reconhecido"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autenticação cancelada"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nenhum PIN, padrão ou senha configurado"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Erro na autenticação"</string>
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Usar bloqueio de tela"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Insira seu bloqueio de tela para continuar"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Pressione o sensor com firmeza"</string>
- <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Não foi possível reconhecer a impressão digital. Tente de novo."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Impressão digital não reconhecida. Tente de novo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Limpe o sensor de impressão digital e tente novamente"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Limpe o sensor e tente novamente"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Pressione o sensor com firmeza"</string>
@@ -1851,7 +1852,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Para sair, deslize de cima para baixo."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Entendi"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Gire a tela para ter uma visualização melhor"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Saia da tela dividida para ter uma visualização melhor"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Abra o app <xliff:g id="NAME">%s</xliff:g> em tela cheia para ter uma melhor visualização"</string>
<string name="done_label" msgid="7283767013231718521">"Concluído"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Controle deslizante circular das horas"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Controle deslizante circular dos minutos"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index e103148cba3b..be339765f85f 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -621,6 +621,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biometric indisponibil"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentificarea a fost anulată"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nu este recunoscut"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Fața nu a fost recunoscută"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autentificarea a fost anulată"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nu este setat un cod PIN, un model sau o parolă"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Eroare la autentificare"</string>
@@ -1851,7 +1852,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Pentru a ieși, glisează de sus în jos."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Am înțeles"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotește pentru o previzualizare mai bună"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Ieși din ecranul împărțit pentru o previzualizare mai bună"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Deschide <xliff:g id="NAME">%s</xliff:g> pe ecran complet pentru o imagine mai bună"</string>
<string name="done_label" msgid="7283767013231718521">"Terminat"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Selector circular pentru ore"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Selector circular pentru minute"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index cbdd5cb6cc86..58b994d13d4a 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -622,13 +622,14 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометрическое оборудование недоступно"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Аутентификация отменена"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Не распознано"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Лицо не распознано."</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Аутентификация отменена"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Укажите PIN-код, пароль или графический ключ"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Ошибка аутентификации."</string>
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Использовать блокировку экрана"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Чтобы продолжить, разблокируйте экран."</string>
- <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Плотно прижмите палец к сканеру."</string>
- <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Не удалось распознать отпечаток пальца. Повторите попытку."</string>
+ <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Плотно прижмите палец к сканеру"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Не удалось распознать отпечаток. Повторите попытку."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Очистите сканер отпечатков пальцев и повторите попытку."</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Очистите сканер и повторите попытку."</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Плотно прижмите палец к сканеру."</string>
@@ -1852,7 +1853,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Чтобы выйти, проведите по экрану сверху вниз."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"ОК"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Поверните, чтобы лучше видеть."</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Выйдите из режима разделения экрана, чтобы лучше видеть."</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Откройте приложение \"<xliff:g id="NAME">%s</xliff:g>\" в полноэкранном режиме, чтобы лучше видеть."</string>
<string name="done_label" msgid="7283767013231718521">"Готово"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Выбор часов на циферблате"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Выбор минут на циферблате"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 7358a9acb3a2..a62caebb5966 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ජීවමිතික දෘඪාංග ලබා ගත නොහැකිය"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"සත්‍යාපනය අවලංගු කළා"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"හඳුනා නොගන්නා ලදී"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"මුහුණ හඳුනා නොගන්නා ලදි"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"සත්‍යාපනය අවලංගු කළා"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"රහස් අංක, රටා, හෝ මුරපද කිසිවක් සකසා නැත"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"සත්‍යාපනය කිරීමේ දෝෂයකි"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"ඉවත් වීමට, ඉහළ සිට පහළට ස්වයිප් කරන්න"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"වැටහුණි"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"වඩා හොඳ දසුනක් සඳහා කරකවන්න"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"වඩා හොඳ දර්ශනයක් සඳහා බෙදුම් තිරයෙන් පිටවන්න"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"වඩා හොඳ දසුනක් සඳහා <xliff:g id="NAME">%s</xliff:g> පූර්ණ තිරයේ විවෘත කරන්න"</string>
<string name="done_label" msgid="7283767013231718521">"අවසන්"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"පැය කවාකාර සර්පනය"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"මිනිත්තු කවාකාර සර්පනය"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 68f0ed4aa429..9c3c655710cb 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -622,6 +622,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrický hardvér nie je k dispozícii"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Overenie bolo zrušené"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nerozpoznané"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Tvár nebola rozpoznaná"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Overenie bolo zrušené"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nie je nastavený PIN, vzor ani heslo"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Chyba overenia"</string>
@@ -1852,7 +1853,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Ukončíte potiahnutím zhora nadol."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Dobre"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Otočte zariadenie pre lepšie zobrazenie"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Ukončite rozdelenú obrazovku pre lepšie zobrazenie"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Otvorte <xliff:g id="NAME">%s</xliff:g> na celej obrazovke pre lepšie zobrazenie"</string>
<string name="done_label" msgid="7283767013231718521">"Hotovo"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Kruhový posúvač hodín"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Kruhový posúvač minút"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 90fa4ae188aa..187e182be329 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -622,6 +622,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Strojna oprema za biometrične podatke ni na voljo"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Preverjanje pristnosti je preklicano"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Ni prepoznano"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Obraz ni prepoznan"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Preverjanje pristnosti je preklicano"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nastavljena ni nobena koda PIN, vzorec ali geslo"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Napaka pri preverjanju pristnosti"</string>
@@ -1852,7 +1853,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Zaprete ga tako, da z vrha s prstom povlečete navzdol."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Razumem"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Zasukajte za boljši pregled."</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Zaprite razdeljeni zaslon za boljši pregled."</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Aplikacijo <xliff:g id="NAME">%s</xliff:g> odprite v celozaslonskem načinu za boljši pregled."</string>
<string name="done_label" msgid="7283767013231718521">"Dokončano"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Okrogli drsnik za ure"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Okrogli drsnik za minute"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 2ad8a50f412b..f1de0626403a 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Nuk ofrohet harduer biometrik"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Vërtetimi u anulua"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nuk njihet"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Fytyra nuk njihet"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Vërtetimi u anulua"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nuk është vendosur kod PIN, motiv ose fjalëkalim"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Gabim gjatë vërtetimit"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Për të dalë, rrëshqit nga lart poshtë."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"E kuptova"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rrotullo për një pamje më të mirë"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Dil nga ekrani i ndarë për një pamje më të mirë"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Hape <xliff:g id="NAME">%s</xliff:g> në ekran të plotë për pamje më të mirë"</string>
<string name="done_label" msgid="7283767013231718521">"U krye"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Rrëshqitësi rrethor i orëve"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Rrëshqitësi rrethor i minutave"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index ab47275db2b8..7c95c9428207 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -621,16 +621,17 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометријски хардвер није доступан"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Потврда идентитета је отказана"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Није препознато"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Лице није препознато"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Потврда идентитета је отказана"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Нисте подесили ни PIN, ни шаблон, ни лозинку"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Грешка при потврди идентитета"</string>
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Користите закључавање екрана"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Употребите закључавање екрана да бисте наставили"</string>
- <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Јако притисните сензор"</string>
+ <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Чврсто притисните сензор"</string>
<string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Препознавање отиска прста није успело. Пробајте поново."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Обришите сензор за отисак прста и пробајте поново"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Обришите сензор и пробајте поново"</string>
- <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Јако притисните сензор"</string>
+ <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Чврсто притисните сензор"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Превише споро сте померили прст. Пробајте поново."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Пробајте са другим отиском прста"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Превише је светло"</string>
@@ -1851,7 +1852,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Да бисте изашли, превуците надоле одозго."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Важи"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Ротирајте ради бољег приказа"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Изађите из подељеног екрана ради бољег приказа"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Отворите апликацију <xliff:g id="NAME">%s</xliff:g> преко целог екрана да бисте боље видели"</string>
<string name="done_label" msgid="7283767013231718521">"Готово"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Кружни клизач за сате"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Кружни клизач за минуте"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 815c9a94193e..8d95142c463d 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrisk maskinvara är inte tillgänglig"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentiseringen avbröts"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Identifierades inte"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Ansiktet känns inte igen"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autentiseringen avbröts"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Pinkod, mönster eller lösenord har inte angetts"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Ett fel uppstod vid autentiseringen"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Svep nedåt från skärmens överkant för att avsluta."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotera för att få en bättre vy"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Stäng delad skärm för att få en bättre vy"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Öppna <xliff:g id="NAME">%s</xliff:g> i fullskärmsläget för att få en bättre vy"</string>
<string name="done_label" msgid="7283767013231718521">"Klart"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Cirkelreglage för timmar"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Cirkelreglage för minuter"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index d2766803787a..5e30e0d29bc9 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Maunzi ya bayometriki hayapatikani"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Imeghairi uthibitishaji"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Hayatambuliki"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Imeshindwa kutambua uso"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Imeghairi uthibitishaji"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Hujaweka pin, mchoro au nenosiri"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Hitilafu imetokea wakati wa uthibitishaji"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Ili kuondoka, telezesha kidole kutoka juu hadi chini."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Nimeelewa"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Zungusha ili upate mwonekano bora"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Funga skrini iliyogawanywa ili upate mwonekano bora"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Fungua <xliff:g id="NAME">%s</xliff:g> kwenye skrini nzima ili uone maudhui kwa urahisi"</string>
<string name="done_label" msgid="7283767013231718521">"Imekamilika"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Kitelezi cha mviringo wa saa"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Kitelezi cha mviringo wa dakika"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index c6a4530ba469..26442bf553e9 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"பயோமெட்ரிக் வன்பொருள் இல்லை"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"அங்கீகரிப்பு ரத்தானது"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"அடையாளங்காணபடவில்லை"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"முகத்தைக் கண்டறிய முடியவில்லை"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"அங்கீகரிப்பு ரத்தானது"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"பின்னோ, பேட்டர்னோ, கடவுச்சொல்லோ அமைக்கப்படவில்லை"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"அங்கீகரிப்பதில் பிழை"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"வெளியேற, மேலிருந்து கீழே ஸ்வைப் செய்யவும்"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"புரிந்தது"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"சிறந்த காட்சிக்கு சுழற்றுங்கள்"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"சிறந்த காட்சிக்கு திரைப் பிரிப்புப் பயன்முறையில் இருந்து வெளியேறுங்கள்"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"சிறந்த காட்சிக்கு, <xliff:g id="NAME">%s</xliff:g> ஆப்ஸை முழுத்திரைப் பயன்முறையில் திறக்கவும்"</string>
<string name="done_label" msgid="7283767013231718521">"முடிந்தது"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"மணிநேர வட்ட வடிவ ஸ்லைடர்"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"நிமிடங்களுக்கான வட்டவடிவ ஸ்லைடர்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 174429cb573e..e546accd8bd9 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"బయోమెట్రిక్ హార్డ్‌వేర్‌ అందుబాటులో లేదు"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"ప్రమాణీకరణ రద్దు చేయబడింది"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"గుర్తించలేదు"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"ముఖం గుర్తించబడలేదు"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"ప్రమాణీకరణ రద్దు చేయబడింది"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"పిన్, ఆకృతి లేదా పాస్‌వర్డ్‌ సెట్ చేయబడలేదు"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"ప్రామాణీకరిస్తున్నప్పుడు ఎర్రర్ ఏర్పడింది"</string>
@@ -1198,7 +1199,7 @@
<string name="whichSendApplicationLabel" msgid="7467813004769188515">"షేర్ చేయి"</string>
<string name="whichSendToApplication" msgid="77101541959464018">"దీన్ని ఉపయోగించి పంపండి"</string>
<string name="whichSendToApplicationNamed" msgid="3385686512014670003">"%1$sని ఉపయోగించి పంపండి"</string>
- <string name="whichSendToApplicationLabel" msgid="3543240188816513303">"పంపు"</string>
+ <string name="whichSendToApplicationLabel" msgid="3543240188816513303">"పంపండి"</string>
<string name="whichHomeApplication" msgid="8276350727038396616">"హోమ్ యాప్‌ను ఎంచుకోండి"</string>
<string name="whichHomeApplicationNamed" msgid="5855990024847433794">"%1$sని హోమ్‌గా ఉపయోగించండి"</string>
<string name="whichHomeApplicationLabel" msgid="8907334282202933959">"చిత్రాన్ని క్యాప్చర్ చేయి"</string>
@@ -1330,7 +1331,7 @@
<string name="sms_short_code_confirm_message" msgid="1385416688897538724">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ఒక మెసేజ్‌ను &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt;కి పంపాలనుకుంటోంది."</string>
<string name="sms_short_code_details" msgid="2723725738333388351">"దీని వలన మీ మొబైల్ ఖాతాకు "<b>"ఛార్జీలు విధించబడవచ్చు"</b>"."</string>
<string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"దీని వలన మీ మొబైల్ ఖాతాకు ఛార్జీలు విధించబడవచ్చు."</b></string>
- <string name="sms_short_code_confirm_allow" msgid="920477594325526691">"పంపు"</string>
+ <string name="sms_short_code_confirm_allow" msgid="920477594325526691">"పంపండి"</string>
<string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"రద్దు చేయండి"</string>
<string name="sms_short_code_remember_choice" msgid="1374526438647744862">"నా ఎంపికను గుర్తుంచుకో"</string>
<string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"మీరు దీన్ని తర్వాత సెట్టింగ్‌లు &gt; అనువర్తనాలులో మార్చవచ్చు"</string>
@@ -1470,7 +1471,7 @@
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"విడ్జెట్‌ను జోడించడం సాధ్యపడలేదు."</string>
<string name="ime_action_go" msgid="5536744546326495436">"వెళ్లు"</string>
<string name="ime_action_search" msgid="4501435960587287668">"సెర్చ్"</string>
- <string name="ime_action_send" msgid="8456843745664334138">"పంపు"</string>
+ <string name="ime_action_send" msgid="8456843745664334138">"పంపండి"</string>
<string name="ime_action_next" msgid="4169702997635728543">"తర్వాత"</string>
<string name="ime_action_done" msgid="6299921014822891569">"పూర్తయింది"</string>
<string name="ime_action_previous" msgid="6548799326860401611">"మునుపటి"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"నిష్క్రమించడానికి, పై నుండి క్రిందికి స్వైప్ చేయండి."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"అర్థమైంది"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"మెరుగైన వీక్షణ కోసం తిప్పండి"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"మెరుగైన వీక్షణ కోసం స్ప్లిట్ స్క్రీన్ నుండి నిష్క్రమించండి"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"మెరుగైన వీక్షణ కోసం <xliff:g id="NAME">%s</xliff:g>‌ను ఫుల్ స్క్రీన్‌లో తెరవండి"</string>
<string name="done_label" msgid="7283767013231718521">"పూర్తయింది"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"గంటల వృత్తాకార స్లయిడర్"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"నిమిషాల వృత్తాకార స్లయిడర్"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index e5f76431d357..292e911fe507 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"ฮาร์ดแวร์ไบโอเมตริกไม่พร้อมใช้งาน"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"ยกเลิกการตรวจสอบสิทธิ์แล้ว"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"ไม่รู้จัก"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"ไม่รู้จักใบหน้า"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"ยกเลิกการตรวจสอบสิทธิ์แล้ว"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"ไม่ได้ตั้ง PIN, รูปแบบ หรือรหัสผ่าน"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"การตรวจสอบข้อผิดพลาด"</string>
@@ -656,7 +657,7 @@
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ปิดใช้เซ็นเซอร์ชั่วคราวแล้ว"</string>
<string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"ใช้เซ็นเซอร์ลายนิ้วมือไม่ได้ โปรดติดต่อผู้ให้บริการซ่อม"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"กดปุ่มเปิด/ปิดแล้ว"</string>
- <string name="fingerprint_name_template" msgid="8941662088160289778">"นิ้ว <xliff:g id="FINGERID">%d</xliff:g>"</string>
+ <string name="fingerprint_name_template" msgid="8941662088160289778">"นิ้วมือ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ใช้ลายนิ้วมือ"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ใช้ลายนิ้วมือหรือการล็อกหน้าจอ"</string>
<string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ใช้ลายนิ้วมือของคุณเพื่อดำเนินการต่อ"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"หากต้องการออก ให้เลื่อนลงจากด้านบน"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"รับทราบ"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"หมุนเพื่อรับมุมมองที่ดียิ่งขึ้น"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"ออกจากโหมดแยกหน้าจอเพื่อรับมุมมองที่ดียิ่งขึ้น"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"เปิด <xliff:g id="NAME">%s</xliff:g> ในโหมดเต็มหน้าจอเพื่อรับมุมมองที่ดียิ่งขึ้น"</string>
<string name="done_label" msgid="7283767013231718521">"เสร็จสิ้น"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"ตัวเลื่อนหมุนระบุชั่วโมง"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"ตัวเลื่อนหมุนระบุนาที"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 1371de6143e9..786c324642a8 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Walang biometric hardware"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Nakansela ang pag-authenticate"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Hindi nakilala"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Hindi nakilala ang mukha"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Nakansela ang pag-authenticate"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Walang itinakdang pin, pattern, o password"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Nagkaroon ng error sa pag-authenticate"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Upang lumabas, mag-swipe mula sa itaas pababa."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Nakuha ko"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"I-rotate para sa mas magandang view"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Lumabas sa split screen para sa mas magandang view"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Buksan ang <xliff:g id="NAME">%s</xliff:g> sa full screen para sa mas magandang view"</string>
<string name="done_label" msgid="7283767013231718521">"Tapos na"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Pabilog na slider ng mga oras"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Pabilog na slider ng mga minuto"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index e73c55d9964e..ac714ed6e891 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biyometrik donanım kullanılamıyor"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Kimlik doğrulama iptal edildi"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Tanınmadı"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Yüz tanınmadı"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Kimlik doğrulama iptal edildi"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"PIN, desen veya şifre seti yok"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Kimlik doğrulama sırasında hata oluştu"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Çıkmak için yukarıdan aşağıya doğru hızlıca kaydırın."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Anladım"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Daha iyi bir görünüm elde etmek için ekranı döndürün"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Daha iyi bir görünüm elde etmek için bölünmüş ekrandan çıkın"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Daha iyi görünüm için <xliff:g id="NAME">%s</xliff:g> uygulamasını açın"</string>
<string name="done_label" msgid="7283767013231718521">"Bitti"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Saat kaydırma çemberi"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Dakika kaydırma çemberi"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index a8345eda3c5f..76292bf55a13 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -622,6 +622,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Біометричне апаратне забезпечення недоступне"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Автентифікацію скасовано"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Не розпізнано"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Обличчя не розпізнано"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Автентифікацію скасовано"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Не вказано PIN-код, ключ або пароль"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Помилка автентифікації"</string>
@@ -1852,7 +1853,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Щоб вийти, проведіть пальцем зверху вниз."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Оберніть для кращого огляду"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Для кращого огляду вийдіть із режиму розділення екрана"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Для кращого огляду відкрийте додаток <xliff:g id="NAME">%s</xliff:g> на весь екран"</string>
<string name="done_label" msgid="7283767013231718521">"Готово"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Вибір годин на циферблаті"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Вибір хвилин на циферблаті"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index a5658a8ce786..e1c275296885 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"بایومیٹرک ہارڈ ویئر دستیاب نہیں ہے"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"تصدیق کا عمل منسوخ ہو گیا"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"تسلیم شدہ نہیں ہے"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"چہرے کی شناخت نہیں ہو سکی"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"تصدیق کا عمل منسوخ ہو گیا"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"کوئی پن، پیٹرن، یا پاس ورڈ سیٹ نہیں ہے"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"خرابی کی توثیق ہو رہی ہے"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"خارج ہونے کیلئے اوپر سے نیچے سوائپ کریں۔"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"سمجھ آ گئی"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"بہتر منظر کے لیے گھمائیں"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"بہتر منظر کے لیے اسپلٹ اسکرین سے باہر نکلیں"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"بہتر منظر کے لیے <xliff:g id="NAME">%s</xliff:g> کو فُل اسکرین میں کھولیں"</string>
<string name="done_label" msgid="7283767013231718521">"ہو گیا"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"گھنٹوں کا سرکلر سلائیڈر"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"منٹس سرکلر سلائیڈر"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 0e749faeb8aa..41e8fef380e1 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrik sensor ishlamayapti"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentifikatsiya bekor qilindi"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Aniqlanmadi"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Yuz aniqlanmadi"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autentifikatsiya bekor qilindi"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"PIN kod, grafik kalit yoki parol sozlanmagan"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Autentifikatsiya amalga oshmadi"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Chiqish uchun tepadan pastga torting."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Yaxshiroq koʻrish uchun kamerani buring"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Yaxshiroq koʻrish uchun ajratilgan ekran rejimidan chiqing"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Yaxshiroq koʻrish uchun butun ekranda <xliff:g id="NAME">%s</xliff:g> ilovasini oching"</string>
<string name="done_label" msgid="7283767013231718521">"Tayyor"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Doiradan soatni tanlang"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Doiradan daqiqani tanlang"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index ac4358ba717e..205eaf950522 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Không có phần cứng sinh trắc học"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Đã hủy xác thực"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Không nhận dạng được"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Không nhận dạng được khuôn mặt"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Đã hủy xác thực"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Chưa đặt mã PIN, hình mở khóa hoặc mật khẩu"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Lỗi khi xác thực"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Để thoát, hãy vuốt từ trên cùng xuống dưới."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Xoay để xem dễ hơn"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Thoát chế độ chia đôi màn hình để xem dễ hơn"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Mở <xliff:g id="NAME">%s</xliff:g> ở chế độ toàn màn hình để xem dễ hơn"</string>
<string name="done_label" msgid="7283767013231718521">"Xong"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Thanh trượt giờ hình tròn"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Thanh trượt phút hình tròn"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index e938fbfa1a66..35d9d9ed90f7 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"生物识别硬件无法使用"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"身份验证已取消"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"无法识别"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"无法识别面孔"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"身份验证已取消"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"未设置任何 PIN 码、图案和密码"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"进行身份验证时出错"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"要退出,请从顶部向下滑动。"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"知道了"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"旋转可改善预览效果"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"退出分屏可改善预览效果"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"在全屏模式下打开“<xliff:g id="NAME">%s</xliff:g>”可改善预览效果"</string>
<string name="done_label" msgid="7283767013231718521">"完成"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"小时转盘"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"分钟转盘"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index b0f4c04d9603..a471a42c5d37 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"無法使用生物識別硬件"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"已取消驗證"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"未能識別"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"無法辨識面孔"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"已取消驗證"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"未設定 PIN、圖案或密碼"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"驗證時發生錯誤"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"由頂部向下滑動即可退出。"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"知道了"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"旋轉以改善預覽效果"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"退出分割螢幕,以改善預覽效果"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"以全螢幕模式開啟「<xliff:g id="NAME">%s</xliff:g>」,以改善預覽效果"</string>
<string name="done_label" msgid="7283767013231718521">"完成"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"小時環形滑桿"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"分鐘環形滑桿"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 14ced72ebf01..4c83d6fb8770 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"無法使用生物特徵辨識硬體"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"已取消驗證"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"無法辨識"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"無法辨識臉孔"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"已取消驗證"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"未設定 PIN 碼、解鎖圖案或密碼"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"驗證時發生錯誤"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"如要退出,請從畫面頂端向下滑動。"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"知道了"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"旋轉螢幕以瀏覽完整的檢視畫面"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"結束分割畫面以全螢幕瀏覽"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"如要享有更優質的預覽體驗,可透過全螢幕模式開啟「<xliff:g id="NAME">%s</xliff:g>」"</string>
<string name="done_label" msgid="7283767013231718521">"完成"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"小時數環狀滑桿"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"分鐘數環狀滑桿"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index e432c69d36de..72b440499996 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -620,6 +620,7 @@
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"I-Biometric hardware ayitholakali"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Ukufakazela ubuqiniso kukhanseliwe"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Akwaziwa"</string>
+ <string name="biometric_face_not_recognized" msgid="5535599455744525200">"Ubuso abaziwa"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Ukufakazela ubuqiniso kukhanseliwe"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Ayikho iphinikhodi, iphethini, noma iphasiwedi esethiwe"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Iphutha lokufakazela ubuqiniso"</string>
@@ -1850,7 +1851,7 @@
<string name="immersive_cling_description" msgid="7092737175345204832">"Ukuze uphume, swayiphela phansi kusuka phezulu."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Ngiyitholile"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Zungezisa ukuze uthole ukubuka okungcono"</string>
- <string name="display_rotation_camera_compat_toast_in_split_screen" msgid="8393302456336805466">"Phuma ekuhlukaniseni isikrini ukuze ubuke kangcono"</string>
+ <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Vula i-<xliff:g id="NAME">%s</xliff:g> kusikrini esigcwele ngokubuka okungcono"</string>
<string name="done_label" msgid="7283767013231718521">"Kwenziwe"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Amahora weslayidi esiyindingilizi"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Amaminithi weslayidi esiyindingilizi"</string>
diff --git a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
new file mode 100644
index 000000000000..a84ac55f0d5a
--- /dev/null
+++ b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2023 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 android.service.notification;
+
+import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@SmallTest
+@RunWith(Parameterized.class)
+public class NotificationRankingUpdateTest {
+
+ private static final String NOTIFICATION_CHANNEL_ID = "test_channel_id";
+ private static final String TEST_KEY = "key";
+
+ private NotificationChannel mNotificationChannel;
+
+ // TODO(b/284297289): remove this flag set once resolved.
+ @Parameterized.Parameters(name = "rankingUpdateAshmem={0}")
+ public static Boolean[] getRankingUpdateAshmem() {
+ return new Boolean[] { true, false };
+ }
+
+ @Parameterized.Parameter
+ public boolean mRankingUpdateAshmem;
+
+ @Before
+ public void setUp() {
+ mNotificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "test channel",
+ NotificationManager.IMPORTANCE_DEFAULT);
+
+ SystemUiSystemPropertiesFlags.TEST_RESOLVER = flag -> {
+ if (flag.mSysPropKey.equals(RANKING_UPDATE_ASHMEM.mSysPropKey)) {
+ return mRankingUpdateAshmem;
+ }
+ return new SystemUiSystemPropertiesFlags.DebugResolver().isEnabled(flag);
+ };
+ }
+
+ @After
+ public void tearDown() {
+ SystemUiSystemPropertiesFlags.TEST_RESOLVER = null;
+ }
+
+ public NotificationListenerService.Ranking createTestRanking(String key, int rank) {
+ NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
+ ranking.populate(
+ /* key= */ key,
+ /* rank= */ rank,
+ /* matchesInterruptionFilter= */ false,
+ /* visibilityOverride= */ 0,
+ /* suppressedVisualEffects= */ 0,
+ mNotificationChannel.getImportance(),
+ /* explanation= */ null,
+ /* overrideGroupKey= */ null,
+ mNotificationChannel,
+ /* overridePeople= */ null,
+ /* snoozeCriteria= */ null,
+ /* showBadge= */ true,
+ /* userSentiment= */ 0,
+ /* hidden= */ false,
+ /* lastAudiblyAlertedMs= */ -1,
+ /* noisy= */ false,
+ /* smartActions= */ null,
+ /* smartReplies= */ null,
+ /* canBubble= */ false,
+ /* isTextChanged= */ false,
+ /* isConversation= */ false,
+ /* shortcutInfo= */ null,
+ /* rankingAdjustment= */ 0,
+ /* isBubble= */ false,
+ /* proposedImportance= */ 0,
+ /* sensitiveContent= */ false
+ );
+ return ranking;
+ }
+
+ @Test
+ public void testRankingUpdate_rankingConstructor() {
+ NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
+ NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
+ new NotificationListenerService.Ranking[]{ranking});
+
+ NotificationListenerService.RankingMap retrievedRankings = rankingUpdate.getRankingMap();
+ NotificationListenerService.Ranking retrievedRanking =
+ new NotificationListenerService.Ranking();
+ assertTrue(retrievedRankings.getRanking(TEST_KEY, retrievedRanking));
+ assertEquals(123, retrievedRanking.getRank());
+ }
+
+ @Test
+ public void testRankingUpdate_parcelConstructor() {
+ NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
+ NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
+ new NotificationListenerService.Ranking[]{ranking});
+
+ Parcel parceledRankingUpdate = Parcel.obtain();
+ rankingUpdate.writeToParcel(parceledRankingUpdate, 0);
+ parceledRankingUpdate.setDataPosition(0);
+
+ NotificationRankingUpdate retrievedRankingUpdate = new NotificationRankingUpdate(
+ parceledRankingUpdate);
+
+ NotificationListenerService.RankingMap retrievedRankings =
+ retrievedRankingUpdate.getRankingMap();
+ assertNotNull(retrievedRankings);
+ assertTrue(retrievedRankingUpdate.isFdNotNullAndClosed());
+ NotificationListenerService.Ranking retrievedRanking =
+ new NotificationListenerService.Ranking();
+ assertTrue(retrievedRankings.getRanking(TEST_KEY, retrievedRanking));
+ assertEquals(123, retrievedRanking.getRank());
+ assertTrue(retrievedRankingUpdate.equals(rankingUpdate));
+ parceledRankingUpdate.recycle();
+ }
+
+ @Test
+ public void testRankingUpdate_emptyParcelInCheck() {
+ NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
+ NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
+ new NotificationListenerService.Ranking[]{ranking});
+
+ Parcel parceledRankingUpdate = Parcel.obtain();
+ rankingUpdate.writeToParcel(parceledRankingUpdate, 0);
+
+ // This will fail to read the parceledRankingUpdate, because the data position hasn't
+ // been reset, so it'll find no data to read.
+ NotificationRankingUpdate retrievedRankingUpdate = new NotificationRankingUpdate(
+ parceledRankingUpdate);
+ assertNull(retrievedRankingUpdate.getRankingMap());
+ }
+
+ @Test
+ public void testRankingUpdate_describeContents() {
+ NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
+ NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
+ new NotificationListenerService.Ranking[]{ranking});
+ assertEquals(0, rankingUpdate.describeContents());
+ }
+
+ @Test
+ public void testRankingUpdate_equals() {
+ NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
+ NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
+ new NotificationListenerService.Ranking[]{ranking});
+ // Reflexive equality.
+ assertTrue(rankingUpdate.equals(rankingUpdate));
+ // Null or wrong class inequality.
+ assertFalse(rankingUpdate.equals(null));
+ assertFalse(rankingUpdate.equals(ranking));
+
+ // Different ranking contents inequality.
+ NotificationListenerService.Ranking ranking2 = createTestRanking(TEST_KEY, 456);
+ NotificationRankingUpdate rankingUpdate2 = new NotificationRankingUpdate(
+ new NotificationListenerService.Ranking[]{ranking2});
+ assertFalse(rankingUpdate.equals(rankingUpdate2));
+
+ // Same ranking contents equality.
+ ranking2 = createTestRanking(TEST_KEY, 123);
+ rankingUpdate2 = new NotificationRankingUpdate(
+ new NotificationListenerService.Ranking[]{ranking2});
+ assertTrue(rankingUpdate.equals(rankingUpdate2));
+ }
+}
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 214125928892..2e3f60441b3a 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -416,7 +416,7 @@
<!-- The radius of the caption menu shadow. -->
<dimen name="desktop_mode_handle_menu_shadow_radius">2dp</dimen>
- <dimen name="freeform_resize_handle">30dp</dimen>
+ <dimen name="freeform_resize_handle">15dp</dimen>
<dimen name="freeform_resize_corner">44dp</dimen>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 146774189490..c48f2fdaf42f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -80,8 +80,7 @@ import android.view.ViewRootImpl;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.window.ScreenCapture;
-import android.window.ScreenCapture.ScreenCaptureListener;
-import android.window.ScreenCapture.ScreenshotSync;
+import android.window.ScreenCapture.SynchronousScreenCaptureListener;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
@@ -1230,10 +1229,11 @@ public class BubbleController implements ConfigurationChangeListener,
/**
* Performs a screenshot that may exclude the bubble layer, if one is present. The screenshot
- * can be access via the supplied {@link ScreenshotSync#get()} asynchronously.
+ * can be access via the supplied {@link SynchronousScreenCaptureListener#getBuffer()}
+ * asynchronously.
*/
public void getScreenshotExcludingBubble(int displayId,
- Pair<ScreenCaptureListener, ScreenshotSync> screenCaptureListener) {
+ SynchronousScreenCaptureListener screenCaptureListener) {
try {
ScreenCapture.CaptureArgs args = null;
if (mStackView != null) {
@@ -1248,7 +1248,7 @@ public class BubbleController implements ConfigurationChangeListener,
}
}
- mWmService.captureDisplay(displayId, args, screenCaptureListener.first);
+ mWmService.captureDisplay(displayId, args, screenCaptureListener);
} catch (RemoteException e) {
Log.e(TAG, "Failed to capture screenshot");
}
@@ -2219,15 +2219,15 @@ public class BubbleController implements ConfigurationChangeListener,
@Override
@Nullable
- public ScreenshotSync getScreenshotExcludingBubble(int displayId) {
- Pair<ScreenCaptureListener, ScreenshotSync> screenCaptureListener =
+ public SynchronousScreenCaptureListener getScreenshotExcludingBubble(int displayId) {
+ SynchronousScreenCaptureListener screenCaptureListener =
ScreenCapture.createSyncCaptureListener();
mMainExecutor.execute(
() -> BubbleController.this.getScreenshotExcludingBubble(displayId,
screenCaptureListener));
- return screenCaptureListener.second;
+ return screenCaptureListener;
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 259f69296ac7..4d329dd5d446 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -16,8 +16,6 @@
package com.android.wm.shell.bubbles;
-import static android.window.ScreenCapture.ScreenshotSync;
-
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
import static java.lang.annotation.ElementType.PARAMETER;
@@ -34,6 +32,7 @@ import android.service.notification.NotificationListenerService.RankingMap;
import android.util.Pair;
import android.util.SparseArray;
import android.window.ScreenCapture.ScreenshotHardwareBuffer;
+import android.window.ScreenCapture.SynchronousScreenCaptureListener;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
@@ -150,13 +149,14 @@ public interface Bubbles {
boolean isAppBubbleTaskId(int taskId);
/**
- * @return a {@link ScreenshotSync} after performing a screenshot that may exclude the bubble
- * layer, if one is present. The underlying {@link ScreenshotHardwareBuffer} can be access via
- * {@link ScreenshotSync#get()} asynchronously and care should be taken to
- * {@link HardwareBuffer#close()} the associated
- * {@link ScreenshotHardwareBuffer#getHardwareBuffer()} when no longer required.
+` * @return a {@link SynchronousScreenCaptureListener} after performing a screenshot that may
+ * exclude the bubble layer, if one is present. The underlying
+ * {@link ScreenshotHardwareBuffer} can be accessed via
+ * {@link SynchronousScreenCaptureListener#getBuffer()} asynchronously and care should be taken
+ * to {@link HardwareBuffer#close()} the associated
+ * {@link ScreenshotHardwareBuffer#getHardwareBuffer()} when no longer required.`
*/
- ScreenshotSync getScreenshotExcludingBubble(int displayId);
+ SynchronousScreenCaptureListener getScreenshotExcludingBubble(int displayId);
/**
* @return a bubble that matches the provided shortcutId, if one exists.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
index ae1f43320c8b..72702e7c2b88 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
@@ -79,7 +79,7 @@ public class DisplayChangeController {
}
/** Query all listeners for changes that should happen on display change. */
- public void dispatchOnDisplayChange(WindowContainerTransaction outWct, int displayId,
+ void dispatchOnDisplayChange(WindowContainerTransaction outWct, int displayId,
int fromRotation, int toRotation, DisplayAreaInfo newDisplayAreaInfo) {
for (OnDisplayChangingListener c : mDisplayChangeListener) {
c.onDisplayChange(displayId, fromRotation, toRotation, newDisplayAreaInfo, outWct);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index f07ea751b044..8353900be0ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -29,6 +29,7 @@ import android.view.Display;
import android.view.IDisplayWindowListener;
import android.view.IWindowManager;
import android.view.InsetsState;
+import android.window.WindowContainerTransaction;
import androidx.annotation.BinderThread;
@@ -85,11 +86,6 @@ public class DisplayController {
}
}
- /** Get the DisplayChangeController. */
- public DisplayChangeController getChangeController() {
- return mChangeController;
- }
-
/**
* Gets a display by id from DisplayManager.
*/
@@ -195,6 +191,26 @@ public class DisplayController {
}
}
+
+ /** Called when a display rotate requested. */
+ public void onDisplayRotateRequested(WindowContainerTransaction wct, int displayId,
+ int fromRotation, int toRotation) {
+ synchronized (mDisplays) {
+ final DisplayRecord dr = mDisplays.get(displayId);
+ if (dr == null) {
+ Slog.w(TAG, "Skipping Display rotate on non-added display.");
+ return;
+ }
+
+ if (dr.mDisplayLayout != null) {
+ dr.mDisplayLayout.rotateTo(dr.mContext.getResources(), toRotation);
+ }
+
+ mChangeController.dispatchOnDisplayChange(
+ wct, displayId, fromRotation, toRotation, null /* newDisplayAreaInfo */);
+ }
+ }
+
private void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
synchronized (mDisplays) {
final DisplayRecord dr = mDisplays.get(displayId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index d016e73cda96..7b1db545663f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -500,36 +500,62 @@ class DesktopTasksController(
request
)
// Check if we should skip handling this transition
+ var reason = ""
val shouldHandleRequest =
when {
// Only handle open or to front transitions
- request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> false
+ request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> {
+ reason = "transition type not handled (${request.type})"
+ false
+ }
// Only handle when it is a task transition
- request.triggerTask == null -> false
+ request.triggerTask == null -> {
+ reason = "triggerTask is null"
+ false
+ }
// Only handle standard type tasks
- request.triggerTask.activityType != ACTIVITY_TYPE_STANDARD -> false
+ request.triggerTask.activityType != ACTIVITY_TYPE_STANDARD -> {
+ reason = "activityType not handled (${request.triggerTask.activityType})"
+ false
+ }
// Only handle fullscreen or freeform tasks
request.triggerTask.windowingMode != WINDOWING_MODE_FULLSCREEN &&
- request.triggerTask.windowingMode != WINDOWING_MODE_FREEFORM -> false
+ request.triggerTask.windowingMode != WINDOWING_MODE_FREEFORM -> {
+ reason = "windowingMode not handled (${request.triggerTask.windowingMode})"
+ false
+ }
// Otherwise process it
else -> true
}
if (!shouldHandleRequest) {
+ KtProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: skipping handleRequest reason=%s",
+ reason
+ )
return null
}
val task: RunningTaskInfo = request.triggerTask
- return when {
+ val result = when {
// If display has tasks stashed, handle as stashed launch
desktopModeTaskRepository.isStashed(task.displayId) -> handleStashedTaskLaunch(task)
// Check if fullscreen task should be updated
task.windowingMode == WINDOWING_MODE_FULLSCREEN -> handleFullscreenTaskLaunch(task)
// Check if freeform task should be updated
task.windowingMode == WINDOWING_MODE_FREEFORM -> handleFreeformTaskLaunch(task)
- else -> null
+ else -> {
+ null
+ }
}
+ KtProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: handleRequest result=%s",
+ result ?: "null"
+ )
+ return result
}
/**
@@ -552,6 +578,7 @@ class DesktopTasksController(
}
private fun handleFreeformTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? {
+ KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFreeformTaskLaunch")
val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId)
if (activeTasks.none { desktopModeTaskRepository.isVisibleTask(it) }) {
KtProtoLog.d(
@@ -568,6 +595,7 @@ class DesktopTasksController(
}
private fun handleFullscreenTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? {
+ KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFullscreenTaskLaunch")
val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId)
if (activeTasks.any { desktopModeTaskRepository.isVisibleTask(it) }) {
KtProtoLog.d(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index fb966c7de925..86b0f33ad54c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -1018,24 +1018,17 @@ public class PipTransition extends PipTransitionController {
private void resetPrevPip(@NonNull TransitionInfo.Change prevPipTaskChange,
@NonNull SurfaceControl.Transaction startTransaction) {
final SurfaceControl leash = prevPipTaskChange.getLeash();
- final Rect bounds = prevPipTaskChange.getEndAbsBounds();
- final Point offset = prevPipTaskChange.getEndRelOffset();
- bounds.offset(-offset.x, -offset.y);
-
- startTransaction.setWindowCrop(leash, null);
- startTransaction.setMatrix(leash, 1, 0, 0, 1);
- startTransaction.setCornerRadius(leash, 0);
- startTransaction.setPosition(leash, bounds.left, bounds.top);
-
- if (mHasFadeOut && prevPipTaskChange.getTaskInfo().isVisible()) {
- if (mPipAnimationController.getCurrentAnimator() != null) {
- mPipAnimationController.getCurrentAnimator().cancel();
- }
- startTransaction.setAlpha(leash, 1);
- }
+ startTransaction.remove(leash);
+
mHasFadeOut = false;
mCurrentPipTaskToken = null;
- mPipOrganizer.onExitPipFinished(prevPipTaskChange.getTaskInfo());
+
+ // clean-up the state in PipTaskOrganizer if the PipTaskOrganizer#onTaskAppeared() hasn't
+ // been called yet with its leash reference now pointing to a new SurfaceControl not
+ // matching the leash of the pip we are removing.
+ if (mPipOrganizer.getSurfaceControl() == leash) {
+ mPipOrganizer.onExitPipFinished(prevPipTaskChange.getTaskInfo());
+ }
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
index fc674a8aa59b..f9332e4bdb2e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
@@ -63,6 +63,15 @@ public class PhonePipKeepClearAlgorithm implements PipKeepClearAlgorithmInterfac
if (pipBoundsState.isImeShowing()) {
insets.bottom -= pipBoundsState.getImeHeight();
}
+ // if PiP is stashed we only adjust the vertical position if it's outside of insets and
+ // ignore all keep clear areas, since it's already on the side
+ if (pipBoundsState.isStashed()) {
+ if (startingBounds.bottom > insets.bottom || startingBounds.top < insets.top) {
+ // bring PiP back to be aligned by bottom inset
+ startingBounds.offset(0, insets.bottom - startingBounds.bottom);
+ }
+ return startingBounds;
+ }
Rect pipBounds = new Rect(startingBounds);
boolean shouldApplyGravity = false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index 2a61445b27ba..b0fa9936d879 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -49,7 +49,7 @@ public enum ShellProtoLogGroup implements IProtoLogGroup {
Consts.TAG_WM_SPLIT_SCREEN),
WM_SHELL_SYSUI_EVENTS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
- WM_SHELL_DESKTOP_MODE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ WM_SHELL_DESKTOP_MODE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM_SHELL),
WM_SHELL_FLOATING_APPS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 8708359d6dd3..b5d4a9d7e8e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -1516,6 +1516,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Legacy transition we need to create divider here, shell transition case we will
// create it on #finishEnterSplitScreen
mSplitLayout.init();
+ } else {
+ // We handle split visibility itself on shell transition, but sometimes we didn't
+ // reset it correctly after dismiss by some reason, so just set invisible before active.
+ setSplitsVisible(false);
}
if (taskInfo != null) {
setSideStagePosition(startPosition, wct);
@@ -2332,7 +2336,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
} else if (!isSplitScreenVisible() && isOpening) {
// If split is running in the background and the trigger task is appearing into
// split, prepare to enter split screen.
- setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, out);
prepareEnterSplitScreen(out);
mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(),
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering);
@@ -2358,7 +2361,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (isOpening && getStageOfTask(triggerTask) != null) {
// One task is appearing into split, prepare to enter split screen.
out = new WindowContainerTransaction();
- setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, out);
prepareEnterSplitScreen(out);
mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(),
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering);
@@ -2654,11 +2656,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (mSplitTransitions.mPendingEnter.mExtraTransitType
== TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) {
+ // Open to side should only be used when split already active and foregorund.
if (mainChild == null && sideChild == null) {
Log.w(TAG, "Launched a task in split, but didn't receive any task in transition.");
- mSplitTransitions.mPendingEnter.cancel((cancelWct, cancelT)
- -> prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, cancelWct));
- mSplitUnsupportedToast.show();
+ // This should happen when the target app is already on front, so just cancel.
+ mSplitTransitions.mPendingEnter.cancel(null);
return true;
}
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java
index ae722208782e..4cfbbd971fe3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java
@@ -20,6 +20,7 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Choreographer.CALLBACK_INSETS_ANIMATION;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -370,8 +371,11 @@ class SplashscreenWindowCreator extends AbsSplashWindowCreator {
mStartingWindowRecordManager.addRecord(taskId, tView);
}
- private void removeWindowInner(View decorView, boolean hideView) {
+ private void removeWindowInner(@NonNull View decorView, boolean hideView) {
requestTopUi(false);
+ if (!decorView.isAttachedToWindow()) {
+ return;
+ }
if (hideView) {
decorView.setVisibility(View.GONE);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index e42c6623cea0..d8a88770072d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -813,6 +813,9 @@ public class Transitions implements RemoteCallable<Transitions>,
track.mReadyTransitions.remove(0);
track.mActiveTransition = ready;
if (ready.mAborted) {
+ if (ready.mStartT != null) {
+ ready.mStartT.apply();
+ }
// finish now since there's nothing to animate. Calls back into processReadyQueue
onFinish(ready, null, null);
return;
@@ -940,10 +943,6 @@ public class Transitions implements RemoteCallable<Transitions>,
/** Aborts a transition. This will still queue it up to maintain order. */
private void onAbort(ActiveTransition transition) {
final Track track = mTracks.get(transition.getTrack());
- // apply immediately since they may be "parallel" operations: We currently we use abort for
- // thing which are independent to other transitions (like starting-window transfer).
- transition.mStartT.apply();
- transition.mFinishT.apply();
transition.mAborted = true;
mTracer.logAborted(transition.mInfo.getDebugId());
@@ -1090,9 +1089,8 @@ public class Transitions implements RemoteCallable<Transitions>,
if (wct == null) {
wct = new WindowContainerTransaction();
}
- mDisplayController.getChangeController().dispatchOnDisplayChange(wct,
- change.getDisplayId(), change.getStartRotation(),
- change.getEndRotation(), null /* newDisplayAreaInfo */);
+ mDisplayController.onDisplayRotateRequested(wct, change.getDisplayId(),
+ change.getStartRotation(), change.getEndRotation());
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
index c33a633f2068..936faa3ee6bf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
@@ -170,6 +170,9 @@ public class TransitionUtil {
if (isOpeningType(mode)) {
t.setAlpha(leash, 0.f);
}
+ // Set the transition leash position to 0 in case the divider leash position being
+ // taking down.
+ t.setPosition(leash, 0, 0);
t.setLayer(leash, Integer.MAX_VALUE);
return;
}
@@ -228,7 +231,11 @@ public class TransitionUtil {
t.reparent(change.getLeash(), leashSurface);
t.setAlpha(change.getLeash(), 1.0f);
t.show(change.getLeash());
- t.setPosition(change.getLeash(), 0, 0);
+ if (!isDividerBar(change)) {
+ // For divider, don't modify its inner leash position when creating the outer leash
+ // for the transition. In case the position being wrong after the transition finished.
+ t.setPosition(change.getLeash(), 0, 0);
+ }
t.setLayer(change.getLeash(), 0);
return leashSurface;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index cea0fcb2a9c3..b217bd39a446 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -143,6 +143,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
mHandler,
mChoreographer,
mDisplay.getDisplayId(),
+ 0 /* taskCornerRadius */,
mDecorationContainerSurface,
mDragPositioningCallback);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 3ccb938867af..e3cb8af31049 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -235,6 +235,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mHandler,
mChoreographer,
mDisplay.getDisplayId(),
+ mRelayoutParams.mCornerRadius,
mDecorationContainerSurface,
mDragPositioningCallback);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 05a75887dfa0..68b63e62f09b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -56,7 +56,7 @@ import com.android.internal.view.BaseIWindow;
*/
class DragResizeInputListener implements AutoCloseable {
private static final String TAG = "DragResizeInputListener";
-
+ private static final float TOP_CORNER_PADDING = 1.5f;
private final IWindowSession mWindowSession = WindowManagerGlobal.getWindowSession();
private final Handler mHandler;
private final Choreographer mChoreographer;
@@ -74,6 +74,7 @@ class DragResizeInputListener implements AutoCloseable {
private int mTaskHeight;
private int mResizeHandleThickness;
private int mCornerSize;
+ private int mTaskCornerRadius;
private Rect mLeftTopCornerBounds;
private Rect mRightTopCornerBounds;
@@ -88,12 +89,14 @@ class DragResizeInputListener implements AutoCloseable {
Handler handler,
Choreographer choreographer,
int displayId,
+ int taskCornerRadius,
SurfaceControl decorationSurface,
DragPositioningCallback callback) {
mInputManager = context.getSystemService(InputManager.class);
mHandler = handler;
mChoreographer = choreographer;
mDisplayId = displayId;
+ mTaskCornerRadius = taskCornerRadius;
mDecorationSurface = decorationSurface;
// Use a fake window as the backing surface is a container layer and we don't want to create
// a buffer layer for it so we can't use ViewRootImpl.
@@ -384,19 +387,67 @@ class DragResizeInputListener implements AutoCloseable {
@DragPositioningCallback.CtrlType
private int calculateResizeHandlesCtrlType(float x, float y) {
int ctrlType = 0;
- if (x < 0) {
+ // mTaskCornerRadius is only used in comparing with corner regions. Comparisons with
+ // sides will use the bounds specified in setGeometry and not go into task bounds.
+ if (x < mTaskCornerRadius) {
ctrlType |= CTRL_TYPE_LEFT;
}
- if (x > mTaskWidth) {
+ if (x > mTaskWidth - mTaskCornerRadius) {
ctrlType |= CTRL_TYPE_RIGHT;
}
- if (y < 0) {
+ if (y < mTaskCornerRadius) {
ctrlType |= CTRL_TYPE_TOP;
}
- if (y > mTaskHeight) {
+ if (y > mTaskHeight - mTaskCornerRadius) {
ctrlType |= CTRL_TYPE_BOTTOM;
}
- return ctrlType;
+ return checkDistanceFromCenter(ctrlType, x, y);
+ }
+
+ // If corner input is not within appropriate distance of corner radius, do not use it.
+ // If input is not on a corner or is within valid distance, return ctrlType.
+ @DragPositioningCallback.CtrlType
+ private int checkDistanceFromCenter(@DragPositioningCallback.CtrlType int ctrlType,
+ float x, float y) {
+ int centerX;
+ int centerY;
+
+ // Determine center of rounded corner circle; this is simply the corner if radius is 0.
+ switch (ctrlType) {
+ case CTRL_TYPE_LEFT | CTRL_TYPE_TOP: {
+ centerX = mTaskCornerRadius;
+ centerY = mTaskCornerRadius;
+ break;
+ }
+ case CTRL_TYPE_LEFT | CTRL_TYPE_BOTTOM: {
+ centerX = mTaskCornerRadius;
+ centerY = mTaskHeight - mTaskCornerRadius;
+ break;
+ }
+ case CTRL_TYPE_RIGHT | CTRL_TYPE_TOP: {
+ centerX = mTaskWidth - mTaskCornerRadius;
+ centerY = mTaskCornerRadius;
+ break;
+ }
+ case CTRL_TYPE_RIGHT | CTRL_TYPE_BOTTOM: {
+ centerX = mTaskWidth - mTaskCornerRadius;
+ centerY = mTaskHeight - mTaskCornerRadius;
+ break;
+ }
+ default: {
+ return ctrlType;
+ }
+ }
+ double distanceFromCenter = Math.hypot(x - centerX, y - centerY);
+
+ // TODO(b/286461778): Remove this when input in top corner gap no longer goes to header
+ float cornerPadding = (ctrlType & CTRL_TYPE_TOP) != 0 ? TOP_CORNER_PADDING : 1;
+
+ if (distanceFromCenter < mTaskCornerRadius + mResizeHandleThickness * cornerPadding
+ && distanceFromCenter >= mTaskCornerRadius) {
+ return ctrlType;
+ }
+ return 0;
}
@DragPositioningCallback.CtrlType
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java
index 4d7e9e450ceb..cc9e26b2c4f1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java
@@ -18,6 +18,9 @@ package com.android.wm.shell.pip.phone;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
@@ -26,10 +29,13 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.pip.PipBoundsState;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import java.util.Set;
@@ -42,6 +48,10 @@ import java.util.Set;
public class PhonePipKeepClearAlgorithmTest extends ShellTestCase {
private PhonePipKeepClearAlgorithm mPipKeepClearAlgorithm;
+
+ @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm;
+ @Mock private PipBoundsState mMockPipBoundsState;
+
private static final Rect DISPLAY_BOUNDS = new Rect(0, 0, 1000, 1000);
@Before
@@ -73,7 +83,6 @@ public class PhonePipKeepClearAlgorithmTest extends ShellTestCase {
@Test
public void findUnoccludedPosition_withCollidingUnrestrictedKeepClearArea_moveBounds() {
- // TODO(b/183746978): update this test to accommodate for the updated algorithm
final Rect inBounds = new Rect(0, 0, 100, 100);
final Rect keepClearRect = new Rect(50, 50, 150, 150);
@@ -93,4 +102,202 @@ public class PhonePipKeepClearAlgorithmTest extends ShellTestCase {
assertEquals(inBounds, outBounds);
}
+
+ @Test
+ public void adjust_withCollidingRestrictedKeepClearArea_moveBounds() {
+ final Rect pipBounds = new Rect(0, 0, 100, 100);
+ final Rect keepClearRect = new Rect(50, 50, 150, 150);
+ when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds);
+ when(mMockPipBoundsState.getRestrictedKeepClearAreas()).thenReturn(Set.of(keepClearRect));
+ doAnswer(invocation -> {
+ Rect arg0 = invocation.getArgument(0);
+ arg0.set(DISPLAY_BOUNDS);
+ return null;
+ }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class));
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(
+ mMockPipBoundsState, mMockPipBoundsAlgorithm);
+
+ assertFalse(outBounds.contains(keepClearRect));
+ }
+
+ @Test
+ public void adjust_withNonCollidingRestrictedKeepClearArea_boundsUnchanged() {
+ final Rect pipBounds = new Rect(0, 0, 100, 100);
+ final Rect keepClearRect = new Rect(100, 100, 150, 150);
+ when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds);
+ when(mMockPipBoundsState.getRestrictedKeepClearAreas()).thenReturn(Set.of(keepClearRect));
+ doAnswer(invocation -> {
+ Rect arg0 = invocation.getArgument(0);
+ arg0.set(DISPLAY_BOUNDS);
+ return null;
+ }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class));
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(
+ mMockPipBoundsState, mMockPipBoundsAlgorithm);
+
+ assertFalse(outBounds.contains(keepClearRect));
+ }
+
+ @Test
+ public void adjust_withCollidingRestrictedKeepClearArea_whileStashed_boundsUnchanged() {
+ final Rect pipBounds = new Rect(0, 0, 100, 100);
+ final Rect keepClearRect = new Rect(50, 50, 150, 150);
+ when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds);
+ when(mMockPipBoundsState.isStashed()).thenReturn(true);
+ when(mMockPipBoundsState.getRestrictedKeepClearAreas()).thenReturn(Set.of(keepClearRect));
+ doAnswer(invocation -> {
+ Rect arg0 = invocation.getArgument(0);
+ arg0.set(DISPLAY_BOUNDS);
+ return null;
+ }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class));
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(
+ mMockPipBoundsState, mMockPipBoundsAlgorithm);
+
+ assertEquals(pipBounds, outBounds);
+ }
+
+ @Test
+ public void adjust_withNonCollidingRestrictedKeepClearArea_whileStashed_boundsUnchanged() {
+ final Rect pipBounds = new Rect(0, 0, 100, 100);
+ final Rect keepClearRect = new Rect(100, 100, 150, 150);
+ when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds);
+ when(mMockPipBoundsState.isStashed()).thenReturn(true);
+ when(mMockPipBoundsState.getRestrictedKeepClearAreas()).thenReturn(Set.of(keepClearRect));
+ doAnswer(invocation -> {
+ Rect arg0 = invocation.getArgument(0);
+ arg0.set(DISPLAY_BOUNDS);
+ return null;
+ }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class));
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(
+ mMockPipBoundsState, mMockPipBoundsAlgorithm);
+
+ assertEquals(pipBounds, outBounds);
+ }
+
+ @Test
+ public void adjust_aboveDisplayBounds_onLeftEdge_appliesBottomLeftGravity() {
+ final Rect pipBounds = new Rect(
+ 0, DISPLAY_BOUNDS.top - 50, 100, DISPLAY_BOUNDS.top + 50);
+ final Rect expected = new Rect(
+ 0, DISPLAY_BOUNDS.bottom - 100, 100, DISPLAY_BOUNDS.bottom);
+ when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds);
+ doAnswer(invocation -> {
+ Rect arg0 = invocation.getArgument(0);
+ arg0.set(DISPLAY_BOUNDS);
+ return null;
+ }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class));
+ when(mMockPipBoundsAlgorithm.getSnapFraction(any(Rect.class))).thenReturn(0f);
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(
+ mMockPipBoundsState, mMockPipBoundsAlgorithm);
+
+ assertEquals(expected, outBounds);
+ }
+
+ @Test
+ public void adjust_belowDisplayBounds_onLeftEdge_appliesBottomLeftGravity() {
+ final Rect pipBounds = new Rect(
+ 0, DISPLAY_BOUNDS.bottom - 50, 100, DISPLAY_BOUNDS.bottom + 50);
+ final Rect expected = new Rect(
+ 0, DISPLAY_BOUNDS.bottom - 100, 100, DISPLAY_BOUNDS.bottom);
+ when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds);
+ doAnswer(invocation -> {
+ Rect arg0 = invocation.getArgument(0);
+ arg0.set(DISPLAY_BOUNDS);
+ return null;
+ }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class));
+ when(mMockPipBoundsAlgorithm.getSnapFraction(any(Rect.class))).thenReturn(3f);
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(
+ mMockPipBoundsState, mMockPipBoundsAlgorithm);
+
+ assertEquals(expected, outBounds);
+ }
+
+ @Test
+ public void adjust_aboveDisplayBounds_onRightEdge_appliesBottomRightGravity() {
+ final Rect pipBounds = new Rect(
+ DISPLAY_BOUNDS.right - 100, DISPLAY_BOUNDS.top - 50,
+ DISPLAY_BOUNDS.right, DISPLAY_BOUNDS.top + 50);
+ final Rect expected = new Rect(
+ DISPLAY_BOUNDS.right - 100, DISPLAY_BOUNDS.bottom - 100,
+ DISPLAY_BOUNDS.right, DISPLAY_BOUNDS.bottom);
+ when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds);
+ doAnswer(invocation -> {
+ Rect arg0 = invocation.getArgument(0);
+ arg0.set(DISPLAY_BOUNDS);
+ return null;
+ }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class));
+ when(mMockPipBoundsAlgorithm.getSnapFraction(any(Rect.class))).thenReturn(1f);
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(
+ mMockPipBoundsState, mMockPipBoundsAlgorithm);
+
+ assertEquals(expected, outBounds);
+ }
+
+ @Test
+ public void adjust_belowDisplayBounds_onRightEdge_appliesBottomRightGravity() {
+ final Rect pipBounds = new Rect(
+ DISPLAY_BOUNDS.right - 100, DISPLAY_BOUNDS.bottom - 50,
+ DISPLAY_BOUNDS.right, DISPLAY_BOUNDS.bottom + 50);
+ final Rect expected = new Rect(
+ DISPLAY_BOUNDS.right - 100, DISPLAY_BOUNDS.bottom - 100,
+ DISPLAY_BOUNDS.right, DISPLAY_BOUNDS.bottom);
+ when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds);
+ doAnswer(invocation -> {
+ Rect arg0 = invocation.getArgument(0);
+ arg0.set(DISPLAY_BOUNDS);
+ return null;
+ }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class));
+ when(mMockPipBoundsAlgorithm.getSnapFraction(any(Rect.class))).thenReturn(2f);
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(
+ mMockPipBoundsState, mMockPipBoundsAlgorithm);
+
+ assertEquals(expected, outBounds);
+ }
+
+ @Test
+ public void adjust_whileStashed_aboveDisplayBounds_alignsToBottomInset() {
+ final Rect pipBounds = new Rect(
+ 0, DISPLAY_BOUNDS.top - 50, 100, DISPLAY_BOUNDS.top + 50);
+ final Rect expected = new Rect(
+ 0, DISPLAY_BOUNDS.bottom - 100, 100, DISPLAY_BOUNDS.bottom);
+ when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds);
+ when(mMockPipBoundsState.isStashed()).thenReturn(true);
+ doAnswer(invocation -> {
+ Rect arg0 = invocation.getArgument(0);
+ arg0.set(DISPLAY_BOUNDS);
+ return null;
+ }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class));
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(
+ mMockPipBoundsState, mMockPipBoundsAlgorithm);
+
+ assertEquals(expected, outBounds);
+ }
+
+ @Test
+ public void adjust_whileStashed_belowDisplayBounds_alignsToBottomInset() {
+ final Rect pipBounds = new Rect(
+ 0, DISPLAY_BOUNDS.bottom - 50, 100, DISPLAY_BOUNDS.bottom + 50);
+ final Rect expected = new Rect(
+ 0, DISPLAY_BOUNDS.bottom - 100, 100, DISPLAY_BOUNDS.bottom);
+ when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds);
+ when(mMockPipBoundsState.isStashed()).thenReturn(true);
+ doAnswer(invocation -> {
+ Rect arg0 = invocation.getArgument(0);
+ arg0.set(DISPLAY_BOUNDS);
+ return null;
+ }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class));
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(
+ mMockPipBoundsState, mMockPipBoundsAlgorithm);
+
+ assertEquals(expected, outBounds);
+ }
}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 16b35ffcabac..a5518eb9f854 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -405,8 +405,17 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
// If the previous frame was dropped we don't need to hold onto it, so
// just keep using the previous frame's structure instead
- if (!wasSkipped(mCurrentFrameInfo)) {
+ if (wasSkipped(mCurrentFrameInfo)) {
+ // Use the oldest skipped frame in case we skip more than a single frame
+ if (!mSkippedFrameInfo) {
+ mSkippedFrameInfo.emplace();
+ mSkippedFrameInfo->vsyncId =
+ mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
+ mSkippedFrameInfo->startTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
+ }
+ } else {
mCurrentFrameInfo = mJankTracker.startFrame();
+ mSkippedFrameInfo.reset();
}
mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo);
@@ -602,10 +611,18 @@ void CanvasContext::draw(bool solelyTextureViewUpdates) {
if (vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) {
const auto inputEventId =
static_cast<int32_t>(mCurrentFrameInfo->get(FrameInfoIndex::InputEventId));
- native_window_set_frame_timeline_info(
- mNativeSurface->getNativeWindow(), frameCompleteNr, vsyncId, inputEventId,
- mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime),
- solelyTextureViewUpdates);
+ const ANativeWindowFrameTimelineInfo ftl = {
+ .frameNumber = frameCompleteNr,
+ .frameTimelineVsyncId = vsyncId,
+ .inputEventId = inputEventId,
+ .startTimeNanos = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime),
+ .useForRefreshRateSelection = solelyTextureViewUpdates,
+ .skippedFrameVsyncId = mSkippedFrameInfo ? mSkippedFrameInfo->vsyncId
+ : UiFrameInfoBuilder::INVALID_VSYNC_ID,
+ .skippedFrameStartTimeNanos =
+ mSkippedFrameInfo ? mSkippedFrameInfo->startTime : 0,
+ };
+ native_window_set_frame_timeline_info(mNativeSurface->getNativeWindow(), ftl);
}
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 5219b5757008..32ac5af94c14 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -366,6 +366,12 @@ private:
ColorMode mColorMode = ColorMode::Default;
float mTargetSdrHdrRatio = 1.f;
+
+ struct SkippedFrameInfo {
+ int64_t vsyncId;
+ int64_t startTime;
+ };
+ std::optional<SkippedFrameInfo> mSkippedFrameInfo;
};
} /* namespace renderthread */
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 38bb447f8a16..b7c972083657 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -4933,8 +4933,12 @@ final public class MediaCodec {
* Called when an output frame has rendered on the output surface.
* <p>
* <strong>Note:</strong> This callback is for informational purposes only: to get precise
- * render timing samples, and can be significantly delayed and batched. Some frames may have
- * been rendered even if there was no callback generated.
+ * render timing samples, and can be significantly delayed and batched. Starting with
+ * Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, a callback will always
+ * be received for each rendered frame providing the MediaCodec is still in the executing
+ * state when the callback is dispatched. Prior to Android
+ * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, some frames may have been
+ * rendered even if there was no callback generated.
*
* @param codec the MediaCodec instance
* @param presentationTimeUs the presentation time (media time) of the frame rendered.
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-or/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-or/strings.xml
index 8b211e790017..567c4bc2220f 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-or/strings.xml
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-or/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="settings_label" msgid="5948970810295631236">"ସେଟିଂସ୍"</string>
+ <string name="settings_label" msgid="5948970810295631236">"ସେଟିଂସ"</string>
</resources>
diff --git a/packages/SettingsLib/res/layout/dialog_with_icon.xml b/packages/SettingsLib/res/layout/dialog_with_icon.xml
index 54f8096b87bf..55d12ebe8553 100644
--- a/packages/SettingsLib/res/layout/dialog_with_icon.xml
+++ b/packages/SettingsLib/res/layout/dialog_with_icon.xml
@@ -41,7 +41,6 @@
android:id="@+id/dialog_with_icon_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:padding="10dp"
android:gravity="center"
style="@style/TextAppearanceSmall"/>
diff --git a/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
index 4ffaf1b0c3e4..2ded3c6e82eb 100644
--- a/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
+++ b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
@@ -14,62 +14,48 @@
limitations under the License.
-->
-<ScrollView
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/user_info_editor"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:id="@+id/user_info_scroll"
- android:padding="16dp">
-
- <LinearLayout
- android:layout_width="match_parent"
+ android:baselineAligned="false"
+ android:orientation="vertical">
+ <FrameLayout
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:baselineAligned="false"
- android:orientation="vertical">
- <TextView
- android:id="@+id/user_info_title"
- android:gravity="center"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/EditUserDialogTitle"
- android:text="@string/user_info_settings_title"
- android:textDirection="locale"/>
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center">
- <ImageView
- android:id="@+id/user_photo"
- android:layout_width="@dimen/user_photo_size_in_user_info_dialog"
- android:layout_height="@dimen/user_photo_size_in_user_info_dialog"
- android:contentDescription="@string/user_image_photo_selector"
- android:scaleType="fitCenter"/>
- <ImageView
- android:id="@+id/add_a_photo_icon"
- android:layout_width="@dimen/add_a_photo_icon_size_in_user_info_dialog"
- android:layout_height="@dimen/add_a_photo_icon_size_in_user_info_dialog"
- android:src="@drawable/add_a_photo_circled"
- android:layout_gravity="bottom|right"/>
- </FrameLayout>
-
- <EditText
- android:id="@+id/user_name"
- android:layout_width="match_parent"
- android:layout_height="@dimen/user_name_height_in_user_info_dialog"
- android:layout_gravity="center"
- android:minWidth="200dp"
- android:layout_marginStart="6dp"
- android:minHeight="@dimen/min_tap_target_size"
- android:ellipsize="end"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textAlignment="viewStart"
- android:inputType="text|textCapWords"
- android:selectAllOnFocus="true"
- android:hint="@string/user_nickname"
- android:maxLength="100"/>
-
- </LinearLayout>
+ android:layout_gravity="center">
+ <ImageView
+ android:id="@+id/user_photo"
+ android:layout_width="@dimen/user_photo_size_in_user_info_dialog"
+ android:layout_height="@dimen/user_photo_size_in_user_info_dialog"
+ android:contentDescription="@string/user_image_photo_selector"
+ android:scaleType="fitCenter"/>
+ <ImageView
+ android:id="@+id/add_a_photo_icon"
+ android:layout_width="@dimen/add_a_photo_icon_size_in_user_info_dialog"
+ android:layout_height="@dimen/add_a_photo_icon_size_in_user_info_dialog"
+ android:src="@drawable/add_a_photo_circled"
+ android:layout_gravity="bottom|right"/>
+ </FrameLayout>
+
+ <EditText
+ android:id="@+id/user_name"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/user_name_height_in_user_info_dialog"
+ android:layout_gravity="center"
+ android:minWidth="200dp"
+ android:layout_marginStart="6dp"
+ android:minHeight="@dimen/min_tap_target_size"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textAlignment="viewStart"
+ android:inputType="text|textCapWords"
+ android:selectAllOnFocus="true"
+ android:hint="@string/user_nickname"
+ android:maxLength="100"/>
+
+</LinearLayout>
-</ScrollView>
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
index e61c8f5ab152..997d1f432a82 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
@@ -59,6 +59,7 @@ public class CreateUserDialogController {
private static final String KEY_IS_ADMIN = "admin_status";
private static final String KEY_ADD_USER_LONG_MESSAGE_DISPLAYED =
"key_add_user_long_message_displayed";
+ public static final int MESSAGE_PADDING = 10;
@Retention(RetentionPolicy.SOURCE)
@IntDef({EXIT_DIALOG, INITIAL_DIALOG, GRANT_ADMIN_DIALOG,
@@ -191,6 +192,7 @@ public class CreateUserDialogController {
cancelCallback.run();
clear();
});
+ mCustomDialogHelper.setMessagePadding(MESSAGE_PADDING);
mUserCreationDialog.setCanceledOnTouchOutside(true);
return mUserCreationDialog;
}
@@ -212,7 +214,6 @@ public class CreateUserDialogController {
}
updateLayout();
});
- return;
}
private void updateLayout() {
@@ -234,7 +235,6 @@ public class CreateUserDialogController {
}
Drawable icon = mActivity.getDrawable(R.drawable.ic_person_add);
mCustomDialogHelper.setVisibility(mCustomDialogHelper.ICON, true)
- .setVisibility(mCustomDialogHelper.TITLE, true)
.setVisibility(mCustomDialogHelper.MESSAGE, true)
.setIcon(icon)
.setButtonEnabled(true)
@@ -248,7 +248,6 @@ public class CreateUserDialogController {
mGrantAdminView.setVisibility(View.VISIBLE);
mCustomDialogHelper
.setVisibility(mCustomDialogHelper.ICON, true)
- .setVisibility(mCustomDialogHelper.TITLE, true)
.setVisibility(mCustomDialogHelper.MESSAGE, true)
.setIcon(mActivity.getDrawable(R.drawable.ic_admin_panel_settings))
.setTitle(R.string.user_grant_admin_title)
@@ -262,8 +261,8 @@ public class CreateUserDialogController {
case EDIT_NAME_DIALOG:
mCustomDialogHelper
.setVisibility(mCustomDialogHelper.ICON, false)
- .setVisibility(mCustomDialogHelper.TITLE, false)
.setVisibility(mCustomDialogHelper.MESSAGE, false)
+ .setTitle(R.string.user_info_settings_title)
.setNegativeButtonText(R.string.back)
.setPositiveButtonText(R.string.done);
mEditUserInfoView.setVisibility(View.VISIBLE);
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
index e55d7eac34df..cd5f59731e7f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
@@ -17,7 +17,6 @@
package com.android.settingslib.users;
import android.app.Activity;
-import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
@@ -31,7 +30,6 @@ import android.view.View;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.ImageView;
-import android.widget.ScrollView;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
@@ -41,6 +39,7 @@ import com.android.settingslib.R;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.drawable.CircleFramedDrawable;
+import com.android.settingslib.utils.CustomDialogHelper;
import java.io.File;
import java.util.function.BiConsumer;
@@ -128,7 +127,7 @@ public class EditUserInfoController {
* codes to take photo/choose photo/crop photo.
*/
public Dialog createDialog(Activity activity, ActivityStarter activityStarter,
- @Nullable Drawable oldUserIcon, String defaultUserName, String title,
+ @Nullable Drawable oldUserIcon, String defaultUserName,
BiConsumer<String, Drawable> successCallback, Runnable cancelCallback) {
LayoutInflater inflater = LayoutInflater.from(activity);
View content = inflater.inflate(R.layout.edit_user_info_dialog_content, null);
@@ -160,10 +159,8 @@ public class EditUserInfoController {
userPhotoView);
}
}
- ScrollView scrollView = content.findViewById(R.id.user_info_scroll);
- scrollView.setClipToOutline(true);
mEditUserInfoDialog = buildDialog(activity, content, userNameView, oldUserIcon,
- defaultUserName, title, successCallback, cancelCallback);
+ defaultUserName, successCallback, cancelCallback);
// Make sure the IME is up.
mEditUserInfoDialog.getWindow()
@@ -181,12 +178,13 @@ public class EditUserInfoController {
}
private Dialog buildDialog(Activity activity, View content, EditText userNameView,
- @Nullable Drawable oldUserIcon, String defaultUserName, String title,
+ @Nullable Drawable oldUserIcon, String defaultUserName,
BiConsumer<String, Drawable> successCallback, Runnable cancelCallback) {
- return new AlertDialog.Builder(activity)
- .setView(content)
- .setCancelable(true)
- .setPositiveButton(android.R.string.ok, (dialog, which) -> {
+ CustomDialogHelper dialogHelper = new CustomDialogHelper(activity);
+ dialogHelper
+ .setTitle(R.string.user_info_settings_title)
+ .addCustomView(content)
+ .setPositiveButton(android.R.string.ok, view -> {
Drawable newUserIcon = mEditUserPhotoController != null
? mEditUserPhotoController.getNewUserPhotoDrawable()
: null;
@@ -201,20 +199,23 @@ public class EditUserInfoController {
if (successCallback != null) {
successCallback.accept(userName, userIcon);
}
+ dialogHelper.getDialog().dismiss();
})
- .setNegativeButton(android.R.string.cancel, (dialog, which) -> {
+ .setBackButton(android.R.string.cancel, view -> {
clear();
if (cancelCallback != null) {
cancelCallback.run();
}
- })
- .setOnCancelListener(dialog -> {
- clear();
- if (cancelCallback != null) {
- cancelCallback.run();
- }
- })
- .create();
+ dialogHelper.getDialog().dismiss();
+ });
+ dialogHelper.getDialog().setOnCancelListener(dialog -> {
+ clear();
+ if (cancelCallback != null) {
+ cancelCallback.run();
+ }
+ dialogHelper.getDialog().dismiss();
+ });
+ return dialogHelper.getDialog();
}
@VisibleForTesting
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/CustomDialogHelper.java b/packages/SettingsLib/src/com/android/settingslib/utils/CustomDialogHelper.java
index de488144be6c..5201b3ddc606 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/CustomDialogHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/CustomDialogHelper.java
@@ -193,6 +193,14 @@ public class CustomDialogHelper {
}
/**
+ * Sets message padding of the dialog.
+ */
+ public CustomDialogHelper setMessagePadding(int dp) {
+ mDialogMessage.setPadding(dp, dp, dp, dp);
+ return this;
+ }
+
+ /**
* Sets icon of the dialog.
*/
public CustomDialogHelper setIcon(Drawable icon) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java
index e989ed27508b..b53807744516 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java
@@ -111,13 +111,13 @@ public class CreateUserDialogControllerTest {
mActivityStarter, true, null,
cancelCallback);
dialog.show();
- assertThat(dialog.findViewById(R.id.user_info_scroll).getVisibility()).isEqualTo(View.GONE);
+ assertThat(dialog.findViewById(R.id.user_info_editor).getVisibility()).isEqualTo(View.GONE);
Button next = dialog.findViewById(R.id.button_ok);
next.performClick();
((RadioButton) dialog.findViewById(R.id.grant_admin_yes)).setChecked(true);
- assertThat(dialog.findViewById(R.id.user_info_scroll).getVisibility()).isEqualTo(View.GONE);
+ assertThat(dialog.findViewById(R.id.user_info_editor).getVisibility()).isEqualTo(View.GONE);
next.performClick();
- assertThat(dialog.findViewById(R.id.user_info_scroll).getVisibility())
+ assertThat(dialog.findViewById(R.id.user_info_editor).getVisibility())
.isEqualTo(View.VISIBLE);
dialog.dismiss();
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
index f760032e4a40..f595cd334105 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
@@ -27,7 +27,6 @@ import static org.mockito.Mockito.when;
import android.app.Activity;
import android.app.AlertDialog;
-import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
@@ -108,7 +107,7 @@ public class EditUserInfoControllerTest {
@Test
public void photoControllerOnActivityResult_whenWaiting_isCalled() {
mController.createDialog(mActivity, mActivityStarter, mCurrentIcon, "test user",
- "title", null, null);
+ null, null);
mController.startingActivityForResult();
Intent resultData = new Intent();
mController.onActivityResult(0, 0, resultData);
@@ -126,9 +125,7 @@ public class EditUserInfoControllerTest {
() -> String.valueOf('A')).limit(200).collect(Collectors.joining());
final AlertDialog dialog = (AlertDialog) mController.createDialog(mActivity,
- mActivityStarter, mCurrentIcon,
- "test user", "title", null,
- null);
+ mActivityStarter, mCurrentIcon, "test user", null, null);
dialog.show();
final EditText userNameEditText = dialog.findViewById(R.id.user_name);
userNameEditText.setText(longName);
@@ -143,7 +140,7 @@ public class EditUserInfoControllerTest {
AlertDialog dialog = (AlertDialog) mController.createDialog(
mActivity, mActivityStarter, mCurrentIcon, "test",
- "title", successCallback, cancelCallback);
+ successCallback, cancelCallback);
dialog.show();
dialog.cancel();
@@ -159,9 +156,9 @@ public class EditUserInfoControllerTest {
AlertDialog dialog = (AlertDialog) mController.createDialog(
mActivity, mActivityStarter, mCurrentIcon, "test",
- "title", successCallback, cancelCallback);
+ successCallback, cancelCallback);
dialog.show();
- dialog.getButton(Dialog.BUTTON_NEGATIVE).performClick();
+ dialog.findViewById(R.id.button_back).performClick();
verifyNoInteractions(successCallback);
verify(cancelCallback, times(1))
@@ -176,11 +173,11 @@ public class EditUserInfoControllerTest {
Drawable oldUserIcon = mCurrentIcon;
AlertDialog dialog = (AlertDialog) mController.createDialog(
mActivity, mActivityStarter, oldUserIcon, "test",
- "title", successCallback, cancelCallback);
+ successCallback, cancelCallback);
// No change to the photo.
when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(null);
dialog.show();
- dialog.getButton(Dialog.BUTTON_POSITIVE).performClick();
+ dialog.findViewById(R.id.button_ok).performClick();
verify(successCallback, times(1))
.accept("test", oldUserIcon);
@@ -194,11 +191,11 @@ public class EditUserInfoControllerTest {
AlertDialog dialog = (AlertDialog) mController.createDialog(
mActivity, mActivityStarter, null, "test",
- "title", successCallback, cancelCallback);
+ successCallback, cancelCallback);
// No change to the photo.
when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(null);
dialog.show();
- dialog.getButton(Dialog.BUTTON_POSITIVE).performClick();
+ dialog.findViewById(R.id.button_ok).performClick();
verify(successCallback, times(1))
.accept("test", null);
@@ -212,14 +209,14 @@ public class EditUserInfoControllerTest {
AlertDialog dialog = (AlertDialog) mController.createDialog(
mActivity, mActivityStarter, mCurrentIcon, "test",
- "title", successCallback, cancelCallback);
+ successCallback, cancelCallback);
// No change to the photo.
when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(null);
dialog.show();
String expectedNewName = "new test user";
EditText editText = (EditText) dialog.findViewById(R.id.user_name);
editText.setText(expectedNewName);
- dialog.getButton(Dialog.BUTTON_POSITIVE).performClick();
+ dialog.findViewById(R.id.button_ok).performClick();
verify(successCallback, times(1))
.accept(expectedNewName, mCurrentIcon);
@@ -233,12 +230,12 @@ public class EditUserInfoControllerTest {
AlertDialog dialog = (AlertDialog) mController.createDialog(
mActivity, mActivityStarter, mCurrentIcon, "test",
- "title", successCallback, cancelCallback);
+ successCallback, cancelCallback);
// A different drawable.
Drawable newPhoto = mock(Drawable.class);
when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(newPhoto);
dialog.show();
- dialog.getButton(Dialog.BUTTON_POSITIVE).performClick();
+ dialog.findViewById(R.id.button_ok).performClick();
verify(successCallback, times(1))
.accept("test", newPhoto);
@@ -252,12 +249,12 @@ public class EditUserInfoControllerTest {
AlertDialog dialog = (AlertDialog) mController.createDialog(
mActivity, mActivityStarter, null, "test",
- "title", successCallback, cancelCallback);
+ successCallback, cancelCallback);
// A different drawable.
Drawable newPhoto = mock(Drawable.class);
when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(newPhoto);
dialog.show();
- dialog.getButton(Dialog.BUTTON_POSITIVE).performClick();
+ dialog.findViewById(R.id.button_ok).performClick();
verify(successCallback, times(1))
.accept("test", newPhoto);
@@ -269,7 +266,7 @@ public class EditUserInfoControllerTest {
mPhotoRestrictedByBase = true;
mController.createDialog(mActivity, mActivityStarter, mCurrentIcon,
- "test", "title", null, null);
+ "test", null, null);
assertThat(mController.mPhotoController).isNull();
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 43f98c38715d..56e0643b1e20 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -839,6 +839,8 @@
<uses-permission android:name="android.permission.LAUNCH_CREDENTIAL_SELECTOR" />
<!-- Permission required for CTS test IntentRedirectionTest -->
<uses-permission android:name="android.permission.QUERY_CLONED_APPS" />
+ <!-- Permission required for accessing all content provider mime types -->
+ <uses-permission android:name="android.permission.GET_ANY_PROVIDER_TYPE" />
<application
android:label="@string/app_label"
diff --git a/packages/Shell/src/com/android/shell/Screenshooter.java b/packages/Shell/src/com/android/shell/Screenshooter.java
index baaddf53bcab..8a5b7daab7e0 100644
--- a/packages/Shell/src/com/android/shell/Screenshooter.java
+++ b/packages/Shell/src/com/android/shell/Screenshooter.java
@@ -21,12 +21,10 @@ import static android.view.Display.DEFAULT_DISPLAY;
import android.graphics.Bitmap;
import android.os.RemoteException;
import android.util.Log;
-import android.util.Pair;
import android.view.WindowManagerGlobal;
import android.window.ScreenCapture;
-import android.window.ScreenCapture.ScreenCaptureListener;
import android.window.ScreenCapture.ScreenshotHardwareBuffer;
-import android.window.ScreenCapture.ScreenshotSync;
+import android.window.ScreenCapture.SynchronousScreenCaptureListener;
/**
* Helper class used to take screenshots.
@@ -46,15 +44,15 @@ final class Screenshooter {
static Bitmap takeScreenshot() {
Log.d(TAG, "Taking fullscreen screenshot");
// Take the screenshot
- final Pair<ScreenCaptureListener, ScreenshotSync> syncScreenCapture =
+ final SynchronousScreenCaptureListener syncScreenCapture =
ScreenCapture.createSyncCaptureListener();
try {
WindowManagerGlobal.getWindowManagerService().captureDisplay(DEFAULT_DISPLAY, null,
- syncScreenCapture.first);
+ syncScreenCapture);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
- final ScreenshotHardwareBuffer screenshotBuffer = syncScreenCapture.second.get();
+ final ScreenshotHardwareBuffer screenshotBuffer = syncScreenCapture.getBuffer();
final Bitmap screenShot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
if (screenShot == null) {
Log.e(TAG, "Failed to take fullscreen screenshot");
diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml
index 40f16871c56f..c89855d2d06c 100644
--- a/packages/SystemUI/res-keyguard/values-ne/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml
@@ -91,9 +91,9 @@
<string name="kg_sim_pin_instructions" msgid="1942424305184242951">"SIM को PIN हाल्नुहोस्।"</string>
<string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" को SIM को PIN हाल्नुहोस्।"</string>
<string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> मोबाइल सेवा बिना डिभाइसको प्रयोग गर्न eSIM लाई असक्षम पार्नुहोस्।"</string>
- <string name="kg_puk_enter_puk_hint" msgid="3005288372875367017">"SIM कार्ड अहिले असक्षम छ। सुचारु गर्नको लागि PUK कोड प्रविष्टि गर्नुहोस्। विवरणको लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।"</string>
- <string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"SIM <xliff:g id="CARRIER">%1$s</xliff:g> अहिले असक्षम छ। सुचारु गर्नको लागि PUK कोड प्रविष्टि गर्नुहोस्। विवरणका लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।"</string>
- <string name="kg_puk_enter_pin_hint" msgid="6028432138916150399">"रूचाइएको PIN कोड प्रविष्टि गर्नुहोस्"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="3005288372875367017">"SIM कार्ड अहिले असक्षम छ। सुचारु गर्नको लागि PUK कोड हाल्नुहोस्। विवरणको लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।"</string>
+ <string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"SIM <xliff:g id="CARRIER">%1$s</xliff:g> अहिले असक्षम छ। सुचारु गर्नको लागि PUK कोड हाल्नुहोस्। विवरणका लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="6028432138916150399">"रूचाइएको PIN कोड हाल्नुहोस्"</string>
<string name="kg_enter_confirm_pin_hint" msgid="4261064020391799132">"रूचाइएको PIN कोड पुष्टि गर्नुहोस्"</string>
<string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"SIM कार्ड अनलक गरिँदै छ…"</string>
<string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"४ देखि ८ वटा नम्बर भएको एउटा PIN टाइप गर्नुहोस्।"</string>
diff --git a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
index a595566ef817..7105721aff70 100644
--- a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
@@ -110,7 +110,9 @@
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:ellipsize="end"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:ellipsize="marquee"
+ android:singleLine="true"
android:maxLines="1"
android:textColor="@color/media_dialog_item_main_content"
android:textSize="14sp"
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 806d1aff3a30..3ca74acf2d66 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -81,9 +81,6 @@ public class SwipeHelper implements Gefingerpoken, Dumpable {
protected final Handler mHandler;
- private float mMinSwipeProgress = 0f;
- private float mMaxSwipeProgress = 1f;
-
private final SpringConfig mSnapBackSpringConfig =
new SpringConfig(SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
@@ -227,18 +224,11 @@ public class SwipeHelper implements Gefingerpoken, Dumpable {
return v.getMeasuredWidth();
}
- public void setMinSwipeProgress(float minSwipeProgress) {
- mMinSwipeProgress = minSwipeProgress;
- }
-
- public void setMaxSwipeProgress(float maxSwipeProgress) {
- mMaxSwipeProgress = maxSwipeProgress;
- }
-
private float getSwipeProgressForOffset(View view, float translation) {
+ if (translation == 0) return 0;
float viewSize = getSize(view);
float result = Math.abs(translation / viewSize);
- return Math.min(Math.max(mMinSwipeProgress, result), mMaxSwipeProgress);
+ return Math.min(Math.max(0, result), 1);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
index 7c90735fc0fc..c48660328bf0 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
@@ -48,6 +48,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
@@ -86,7 +87,7 @@ constructor(
}
val keyguardAuthenticated: Flow<Boolean> = repository.keyguardAuthenticated.filterNotNull()
- val isShowing: Flow<Boolean> = repository.primaryBouncerShow
+ val isShowing: StateFlow<Boolean> = repository.primaryBouncerShow
val startingToHide: Flow<Unit> = repository.primaryBouncerStartingToHide.filter { it }.map {}
val isBackButtonEnabled: Flow<Boolean> = repository.isBackButtonEnabled.filterNotNull()
val showMessage: Flow<BouncerShowMessageModel> = repository.showMessage.filterNotNull()
@@ -383,8 +384,9 @@ constructor(
mainHandler.removeCallbacks(showRunnable)
}
- private fun isBouncerShowing(): Boolean {
- return repository.primaryBouncerShow.value
+ /** Returns whether the primary bouncer is currently showing. */
+ fun isBouncerShowing(): Boolean {
+ return isShowing.value
}
/** Whether we want to wait to show the bouncer in case passive auth succeeds. */
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index b2bcb0554b0d..25ccc1658744 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -396,6 +396,7 @@ public class BrightLineFalsingManager implements FalsingManager {
|| mDataProvider.isDocked()
|| mAccessibilityManager.isTouchExplorationEnabled()
|| mDataProvider.isA11yAction()
+ || mDataProvider.isFromTrackpad()
|| (mFeatureFlags.isEnabled(Flags.FALSING_OFF_FOR_UNFOLDED)
&& mDataProvider.isUnfolded());
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
index d6c85fbaf5a9..809d5b289cab 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
@@ -261,6 +261,16 @@ public class FalsingDataProvider {
return mLastMotionEvent.getY() < mFirstRecentMotionEvent.getY();
}
+ public boolean isFromTrackpad() {
+ if (mRecentMotionEvents.isEmpty()) {
+ return false;
+ }
+
+ int classification = mRecentMotionEvents.get(0).getClassification();
+ return classification == MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE
+ || classification == MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE;
+ }
+
private void recalculateData() {
if (!mDirty) {
return;
@@ -343,7 +353,9 @@ public class FalsingDataProvider {
motionEvent.getDeviceId(),
motionEvent.getEdgeFlags(),
motionEvent.getSource(),
- motionEvent.getFlags()
+ motionEvent.getDisplayId(),
+ motionEvent.getFlags(),
+ motionEvent.getClassification()
));
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index e64ee28d8e71..d173fe5aef03 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -259,6 +259,11 @@ object Flags {
@JvmField
val FP_LISTEN_OCCLUDING_APPS = unreleasedFlag(237, "fp_listen_occluding_apps")
+ /** Flag meant to guard the talkback fix for the KeyguardIndicationTextView */
+ // TODO(b/286563884): Tracking bug
+ @JvmField
+ val KEYGUARD_TALKBACK_FIX = unreleasedFlag(238, "keyguard_talkback_fix")
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -733,4 +738,10 @@ object Flags {
@JvmField
val BIGPICTURE_NOTIFICATION_LAZY_LOADING =
unreleasedFlag(283447257, "bigpicture_notification_lazy_loading")
+
+ // 2900 - CentralSurfaces-related flags
+
+ // TODO(b/285174336): Tracking Bug
+ @JvmField
+ val USE_REPOS_FOR_BOUNCER_SHOWING = unreleasedFlag(2900, "use_repos_for_bouncer_showing")
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 88ffa8da666b..318cd99a06ed 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -24,7 +24,6 @@ import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -175,9 +174,8 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
mCurrentActivePosition = position;
updateFullItemClickListener(v -> onItemClick(v, device));
setSingleLineLayout(getItemTitle(device));
- initMutingExpectedDevice();
- } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
- && device.hasSubtext()) {
+ initFakeActiveDevice();
+ } else if (device.hasSubtext()) {
boolean isActiveWithOngoingSession =
(device.hasOngoingSession() && (currentlyConnected || isDeviceIncluded(
mController.getSelectedMediaDevice(), device)));
@@ -267,6 +265,27 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
setUpDeviceIcon(device);
updateFullItemClickListener(v -> cancelMuteAwaitConnection());
setSingleLineLayout(getItemTitle(device));
+ } else if (device.hasOngoingSession()) {
+ mCurrentActivePosition = position;
+ if (device.isHostForOngoingSession()) {
+ updateTitleIcon(R.drawable.media_output_icon_volume,
+ mController.getColorItemContent());
+ updateEndClickAreaAsSessionEditing(device);
+ mEndClickIcon.setVisibility(View.VISIBLE);
+ setSingleLineLayout(getItemTitle(device), true /* showSeekBar */,
+ false /* showProgressBar */, false /* showCheckBox */,
+ true /* showEndTouchArea */);
+ initSeekbar(device, isCurrentSeekbarInvisible);
+ } else {
+ updateDeviceStatusIcon(mContext.getDrawable(
+ R.drawable.ic_sound_bars_anim));
+ mStatusIcon.setVisibility(View.VISIBLE);
+ updateSingleLineLayoutContentAlpha(
+ updateClickActionBasedOnSelectionBehavior(device)
+ ? DEVICE_CONNECTED_ALPHA : DEVICE_DISCONNECTED_ALPHA);
+ setSingleLineLayout(getItemTitle(device));
+ initFakeActiveDevice();
+ }
} else if (mController.isCurrentConnectedDeviceRemote()
&& !mController.getSelectableMediaDevice().isEmpty()) {
//If device is connected and there's other selectable devices, layout as
@@ -351,7 +370,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
ColorStateList.valueOf(mController.getColorItemContent()));
mEndClickIcon.setOnClickListener(
v -> mController.tryToLaunchInAppRoutingIntent(device.getId(), v));
- mEndTouchArea.setOnClickListener(v -> mCheckBox.performClick());
+ mEndTouchArea.setOnClickListener(v -> mEndClickIcon.performClick());
}
public void updateEndClickAreaColor(int color) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 01f790422bc3..b88eba9081f7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -188,6 +188,7 @@ public abstract class MediaOutputBaseAdapter extends
mContainerLayout.setContentDescription(null);
mTitleText.setTextColor(mController.getColorItemContent());
mSubTitleText.setTextColor(mController.getColorItemContent());
+ mSubTitleText.setSelected(true);
mTwoLineTitleText.setTextColor(mController.getColorItemContent());
mVolumeValueText.setTextColor(mController.getColorItemContent());
mSeekBar.setProgressTintList(
@@ -417,7 +418,7 @@ public abstract class MediaOutputBaseAdapter extends
mIconAreaLayout.setOnClickListener(listener);
}
- void initMutingExpectedDevice() {
+ void initFakeActiveDevice() {
disableSeekBar();
updateTitleIcon(R.drawable.media_output_icon_volume,
mController.getColorItemContent());
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 5e9406c9d507..cf192f9a1272 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -254,8 +254,9 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
private boolean mDeferSetIsOnLeftEdge;
private boolean mIsAttached;
- private boolean mIsGesturalModeEnabled;
+ private boolean mIsGestureHandlingEnabled;
private boolean mIsTrackpadConnected;
+ private boolean mInGestureNavMode;
private boolean mUsingThreeButtonNav;
private boolean mIsEnabled;
private boolean mIsNavBarShownTransiently;
@@ -562,9 +563,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
mInputManager.registerInputDeviceListener(mInputDeviceListener, mMainHandler);
int [] inputDevices = mInputManager.getInputDeviceIds();
for (int inputDeviceId : inputDevices) {
- if (isTrackpadDevice(inputDeviceId)) {
- mIsTrackpadConnected = true;
- }
+ mInputDeviceListener.onInputDeviceAdded(inputDeviceId);
}
}
updateIsEnabled();
@@ -589,8 +588,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
*/
public void onNavigationModeChanged(int mode) {
mUsingThreeButtonNav = QuickStepContract.isLegacyMode(mode);
- mIsGesturalModeEnabled = QuickStepContract.isGesturalMode(mode) || (
- mIsTrackpadGestureFeaturesEnabled && mUsingThreeButtonNav && mIsTrackpadConnected);
+ mInGestureNavMode = QuickStepContract.isGesturalMode(mode);
updateIsEnabled();
updateCurrentUserResources();
}
@@ -613,79 +611,81 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
private void updateIsEnabled() {
try {
Trace.beginSection("EdgeBackGestureHandler#updateIsEnabled");
- updateIsEnabledTraced();
- } finally {
- Trace.endSection();
- }
- }
-
- private void updateIsEnabledTraced() {
- boolean isEnabled = mIsAttached && mIsGesturalModeEnabled;
- if (isEnabled == mIsEnabled) {
- return;
- }
- mIsEnabled = isEnabled;
- disposeInputChannel();
-
- if (mEdgeBackPlugin != null) {
- mEdgeBackPlugin.onDestroy();
- mEdgeBackPlugin = null;
- }
- if (!mIsEnabled) {
- mGestureNavigationSettingsObserver.unregister();
- if (DEBUG_MISSING_GESTURE) {
- Log.d(DEBUG_MISSING_GESTURE_TAG, "Unregister display listener");
+ mIsGestureHandlingEnabled =
+ mInGestureNavMode || (mIsTrackpadGestureFeaturesEnabled && mUsingThreeButtonNav
+ && mIsTrackpadConnected);
+ boolean isEnabled = mIsAttached && mIsGestureHandlingEnabled;
+ if (isEnabled == mIsEnabled) {
+ return;
}
- mPluginManager.removePluginListener(this);
- TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
- DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
- mPipOptional.ifPresent(pip -> pip.setOnIsInPipStateChangedListener(null));
+ mIsEnabled = isEnabled;
+ disposeInputChannel();
- try {
- mWindowManagerService.unregisterSystemGestureExclusionListener(
- mGestureExclusionListener, mDisplayId);
- } catch (RemoteException | IllegalArgumentException e) {
- Log.e(TAG, "Failed to unregister window manager callbacks", e);
+ if (mEdgeBackPlugin != null) {
+ mEdgeBackPlugin.onDestroy();
+ mEdgeBackPlugin = null;
}
- } else {
- mGestureNavigationSettingsObserver.register();
- updateDisplaySize();
- if (DEBUG_MISSING_GESTURE) {
- Log.d(DEBUG_MISSING_GESTURE_TAG, "Register display listener");
- }
- TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
- mMainExecutor::execute, mOnPropertiesChangedListener);
- mPipOptional.ifPresent(
- pip -> pip.setOnIsInPipStateChangedListener(mOnIsInPipStateChangedListener));
- mDesktopModeOptional.ifPresent(
- dm -> dm.addDesktopGestureExclusionRegionListener(
- mDesktopCornersChangedListener, mMainExecutor));
+ if (!mIsEnabled) {
+ mGestureNavigationSettingsObserver.unregister();
+ if (DEBUG_MISSING_GESTURE) {
+ Log.d(DEBUG_MISSING_GESTURE_TAG, "Unregister display listener");
+ }
+ mPluginManager.removePluginListener(this);
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
+ mTaskStackListener);
+ DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
+ mPipOptional.ifPresent(pip -> pip.setOnIsInPipStateChangedListener(null));
+
+ try {
+ mWindowManagerService.unregisterSystemGestureExclusionListener(
+ mGestureExclusionListener, mDisplayId);
+ } catch (RemoteException | IllegalArgumentException e) {
+ Log.e(TAG, "Failed to unregister window manager callbacks", e);
+ }
- try {
- mWindowManagerService.registerSystemGestureExclusionListener(
- mGestureExclusionListener, mDisplayId);
- } catch (RemoteException | IllegalArgumentException e) {
- Log.e(TAG, "Failed to register window manager callbacks", e);
- }
+ } else {
+ mGestureNavigationSettingsObserver.register();
+ updateDisplaySize();
+ if (DEBUG_MISSING_GESTURE) {
+ Log.d(DEBUG_MISSING_GESTURE_TAG, "Register display listener");
+ }
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(
+ mTaskStackListener);
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+ mMainExecutor::execute, mOnPropertiesChangedListener);
+ mPipOptional.ifPresent(pip -> pip.setOnIsInPipStateChangedListener(
+ mOnIsInPipStateChangedListener));
+ mDesktopModeOptional.ifPresent(
+ dm -> dm.addDesktopGestureExclusionRegionListener(
+ mDesktopCornersChangedListener, mMainExecutor));
+
+ try {
+ mWindowManagerService.registerSystemGestureExclusionListener(
+ mGestureExclusionListener, mDisplayId);
+ } catch (RemoteException | IllegalArgumentException e) {
+ Log.e(TAG, "Failed to register window manager callbacks", e);
+ }
- // Register input event receiver
- mInputMonitor = mContext.getSystemService(InputManager.class).monitorGestureInput(
- "edge-swipe", mDisplayId);
- mInputEventReceiver = new InputChannelCompat.InputEventReceiver(
- mInputMonitor.getInputChannel(), Looper.getMainLooper(),
- Choreographer.getInstance(), this::onInputEvent);
-
- // Add a nav bar panel window
- mIsNewBackAffordanceEnabled = mFeatureFlags.isEnabled(Flags.NEW_BACK_AFFORDANCE);
- resetEdgeBackPlugin();
- mPluginManager.addPluginListener(
- this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
+ // Register input event receiver
+ mInputMonitor = mContext.getSystemService(InputManager.class).monitorGestureInput(
+ "edge-swipe", mDisplayId);
+ mInputEventReceiver = new InputChannelCompat.InputEventReceiver(
+ mInputMonitor.getInputChannel(), Looper.getMainLooper(),
+ Choreographer.getInstance(), this::onInputEvent);
+
+ // Add a nav bar panel window
+ mIsNewBackAffordanceEnabled = mFeatureFlags.isEnabled(Flags.NEW_BACK_AFFORDANCE);
+ resetEdgeBackPlugin();
+ mPluginManager.addPluginListener(
+ this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
+ }
+ // Update the ML model resources.
+ updateMLModelState();
+ } finally {
+ Trace.endSection();
}
- // Update the ML model resources.
- updateMLModelState();
}
@Override
@@ -762,9 +762,9 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
}
private void updateMLModelState() {
- boolean newState =
- mIsGesturalModeEnabled && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.USE_BACK_GESTURE_ML_MODEL, false);
+ boolean newState = mIsGestureHandlingEnabled && DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.USE_BACK_GESTURE_ML_MODEL, false);
if (newState == mUseMLModel) {
return;
@@ -1234,7 +1234,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
pw.println(" mIsEnabled=" + mIsEnabled);
pw.println(" mIsAttached=" + mIsAttached);
pw.println(" mIsBackGestureAllowed=" + mIsBackGestureAllowed);
- pw.println(" mIsGesturalModeEnabled=" + mIsGesturalModeEnabled);
+ pw.println(" mIsGestureHandlingEnabled=" + mIsGestureHandlingEnabled);
pw.println(" mIsNavBarShownTransiently=" + mIsNavBarShownTransiently);
pw.println(" mGestureBlockingActivityRunning=" + mGestureBlockingActivityRunning);
pw.println(" mAllowGesture=" + mAllowGesture);
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java
index 3e78489e5707..9962c914b476 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java
@@ -16,10 +16,6 @@
package com.android.systemui.screenrecord;
-import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.INTERNAL;
-import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC;
-import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC_AND_INTERNAL;
-
import android.content.Context;
import android.content.res.Resources;
import android.view.LayoutInflater;
@@ -40,9 +36,6 @@ public class ScreenRecordingAdapter extends ArrayAdapter<ScreenRecordingAudioSou
private LinearLayout mSelectedMic;
private LinearLayout mSelectedInternal;
private LinearLayout mSelectedMicAndInternal;
- private LinearLayout mMicOption;
- private LinearLayout mMicAndInternalOption;
- private LinearLayout mInternalOption;
public ScreenRecordingAdapter(Context context, int resource,
List<ScreenRecordingAudioSource> objects) {
@@ -54,28 +47,21 @@ public class ScreenRecordingAdapter extends ArrayAdapter<ScreenRecordingAudioSou
mSelectedInternal = getSelected(R.string.screenrecord_device_audio_label);
mSelectedMic = getSelected(R.string.screenrecord_mic_label);
mSelectedMicAndInternal = getSelected(R.string.screenrecord_device_audio_and_mic_label);
-
- mMicOption = getOption(R.string.screenrecord_mic_label, Resources.ID_NULL);
- mMicOption.removeViewAt(1);
-
- mMicAndInternalOption = getOption(
- R.string.screenrecord_device_audio_and_mic_label, Resources.ID_NULL);
- mMicAndInternalOption.removeViewAt(1);
-
- mInternalOption = getOption(R.string.screenrecord_device_audio_label,
- R.string.screenrecord_device_audio_description);
}
private LinearLayout getOption(int label, int description) {
- LayoutInflater inflater = (LayoutInflater) getContext()
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ LayoutInflater inflater = LayoutInflater.from(getContext());
LinearLayout layout = (LinearLayout) inflater
.inflate(R.layout.screen_record_dialog_audio_source, null, false);
((TextView) layout.findViewById(R.id.screen_recording_dialog_source_text))
.setText(label);
- if (description != Resources.ID_NULL)
- ((TextView) layout.findViewById(R.id.screen_recording_dialog_source_description))
- .setText(description);
+ TextView descriptionView = layout.findViewById(
+ R.id.screen_recording_dialog_source_description);
+ if (description != Resources.ID_NULL) {
+ descriptionView.setText(description);
+ } else {
+ descriptionView.setVisibility(View.GONE);
+ }
return layout;
}
@@ -92,11 +78,13 @@ public class ScreenRecordingAdapter extends ArrayAdapter<ScreenRecordingAudioSou
public View getDropDownView(int position, View convertView, ViewGroup parent) {
switch (getItem(position)) {
case INTERNAL:
- return mInternalOption;
+ return getOption(R.string.screenrecord_device_audio_label,
+ R.string.screenrecord_device_audio_description);
case MIC_AND_INTERNAL:
- return mMicAndInternalOption;
+ return getOption(
+ R.string.screenrecord_device_audio_and_mic_label, Resources.ID_NULL);
case MIC:
- return mMicOption;
+ return getOption(R.string.screenrecord_mic_label, Resources.ID_NULL);
default:
return super.getDropDownView(position, convertView, parent);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
index 2e47ab6417ae..24fe7d593b3d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
@@ -42,8 +42,8 @@ open class ImageCaptureImpl @Inject constructor(
.setSourceCrop(crop)
.build()
val syncScreenCapture = ScreenCapture.createSyncCaptureListener()
- windowManager.captureDisplay(displayId, captureArgs, syncScreenCapture.first)
- val buffer = syncScreenCapture.second.get()
+ windowManager.captureDisplay(displayId, captureArgs, syncScreenCapture)
+ val buffer = syncScreenCapture.getBuffer()
return buffer?.asBitmap()
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperService.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperService.java
index e0b9f9b7ad93..4cc2f6269bb2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperService.java
@@ -20,7 +20,7 @@ import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.window.ScreenCapture.ScreenshotHardwareBuffer;
-import android.window.ScreenCapture.ScreenshotSync;
+import android.window.ScreenCapture.SynchronousScreenCaptureListener;
import androidx.annotation.Nullable;
@@ -59,9 +59,9 @@ public class AppClipsScreenshotHelperService extends Service {
return null;
}
- ScreenshotSync screenshotSync =
+ SynchronousScreenCaptureListener screenshotSync =
mOptionalBubbles.get().getScreenshotExcludingBubble(displayId);
- ScreenshotHardwareBuffer screenshotHardwareBuffer = screenshotSync.get();
+ ScreenshotHardwareBuffer screenshotHardwareBuffer = screenshotSync.getBuffer();
if (screenshotHardwareBuffer == null) {
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 72941372fc1c..6d00a2affa42 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1659,10 +1659,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding));
mKeyguardNotificationBottomPadding = bottomPadding;
- float staticTopPadding = mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding()
- // getMinStackScrollerPadding is from the top of the screen,
- // but we need it from the top of the NSSL.
- - mNotificationStackScrollLayoutController.getTop();
+ float staticTopPadding = mClockPositionAlgorithm.getLockscreenNotifPadding(
+ mNotificationStackScrollLayoutController.getTop());
+
mKeyguardNotificationTopPadding = staticTopPadding;
// To debug the available space, enable debug lines in this class. If you change how the
@@ -1676,8 +1675,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
Log.i(TAG, "\n");
Log.i(TAG, "staticTopPadding[" + staticTopPadding
+ "] = Clock.padding["
- + mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding()
- + "] - NSSLC.top[" + mNotificationStackScrollLayoutController.getTop()
+ + mClockPositionAlgorithm.getLockscreenNotifPadding(
+ mNotificationStackScrollLayoutController.getTop())
+ "]"
);
Log.i(TAG, "bottomPadding[" + bottomPadding
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
index 7dff6ea99029..e5b84bd86514 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
@@ -55,6 +55,7 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
private Consumer<QS> mQSFragmentAttachedListener = qs -> {};
private QS mQs;
private View mQSContainer;
+ private int mLastQSPaddingBottom;
/**
* These are used to compute the bounding box containing the shade and the notification scrim,
@@ -83,6 +84,10 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
mQs = (QS) fragment;
mQSFragmentAttachedListener.accept(mQs);
mQSContainer = mQs.getView().findViewById(R.id.quick_settings_container);
+ // We need to restore the bottom padding as the fragment may have been recreated due to
+ // some special Configuration change, so we apply the last known padding (this will be
+ // correct even if it has changed while the fragment was destroyed and re-created).
+ setQSContainerPaddingBottom(mLastQSPaddingBottom);
}
@Override
@@ -109,6 +114,7 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
}
public void setQSContainerPaddingBottom(int paddingBottom) {
+ mLastQSPaddingBottom = paddingBottom;
if (mQSContainer != null) {
mQSContainer.setPadding(
mQSContainer.getPaddingLeft(),
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
index 9b797a7e2571..ee4e98e094fc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
@@ -22,6 +22,7 @@ import android.provider.Settings
import android.view.GestureDetector
import android.view.MotionEvent
import com.android.systemui.Dumpable
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.FalsingManager
@@ -29,7 +30,6 @@ import com.android.systemui.plugins.FalsingManager.LOW_PENALTY
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
import com.android.systemui.tuner.TunerService
import com.android.systemui.tuner.TunerService.Tunable
import java.io.PrintWriter
@@ -44,9 +44,8 @@ import javax.inject.Inject
* screen is still ON and not in the true AoD display state. When the device is in the true AoD
* display state, wake-ups are handled by [com.android.systemui.doze.DozeSensors].
*/
-@CentralSurfacesComponent.CentralSurfacesScope
+@SysUISingleton
class PulsingGestureListener @Inject constructor(
- private val notificationShadeWindowView: NotificationShadeWindowView,
private val falsingManager: FalsingManager,
private val dockManager: DockManager,
private val powerInteractor: PowerInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index 7c161d518adf..4b2a65f36aa1 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -37,6 +37,10 @@ import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.statusbar.NotificationShelf
+import com.android.systemui.statusbar.NotificationShelfController
+import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent
+import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.phone.TapAgainView
@@ -49,6 +53,7 @@ import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import javax.inject.Named
+import javax.inject.Provider
/** Module for classes related to the notification shade. */
@Module
@@ -102,6 +107,32 @@ abstract class ShadeModule {
return notificationShadeWindowView.findViewById(R.id.notification_stack_scroller)
}
+ @Provides
+ @SysUISingleton
+ fun providesNotificationShelfController(
+ featureFlags: FeatureFlags,
+ newImpl: Provider<NotificationShelfViewBinderWrapperControllerImpl>,
+ notificationShelfComponentBuilder: NotificationShelfComponent.Builder,
+ layoutInflater: LayoutInflater,
+ notificationStackScrollLayout: NotificationStackScrollLayout,
+ ): NotificationShelfController {
+ return if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+ newImpl.get()
+ } else {
+ val shelfView =
+ layoutInflater.inflate(
+ R.layout.status_bar_notification_shelf,
+ notificationStackScrollLayout,
+ false
+ ) as NotificationShelf
+ val component =
+ notificationShelfComponentBuilder.notificationShelf(shelfView).build()
+ val notificationShelfController = component.notificationShelfController
+ notificationShelfController.init()
+ notificationShelfController
+ }
+ }
+
// TODO(b/277762009): Only allow this view's controller to inject the view. See above.
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index 1c9bc60deefe..5dcf6d1d8f28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -43,6 +43,11 @@ public interface NotificationPresenter extends ExpandableNotificationRow.OnExpan
void onUserSwitched(int newUserId);
/**
+ * Called when a new row is created and bound to a notification.
+ */
+ void onBindRow(ExpandableNotificationRow row);
+
+ /**
* @return true iff the device is in vr mode
*/
boolean isDeviceInVrMode();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index b116246586b5..fb16d6ee6b01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -61,6 +61,7 @@ import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.Pair;
import androidx.annotation.NonNull;
@@ -513,10 +514,16 @@ public class NotifCollection implements Dumpable, PipelineDumpable {
* @return True if the notification was removed, false otherwise.
*/
private boolean tryRemoveNotification(NotificationEntry entry) {
- if (mNotificationSet.get(entry.getKey()) != entry) {
+ final NotificationEntry storedEntry = mNotificationSet.get(entry.getKey());
+ if (storedEntry == null) {
+ Log.wtf(TAG, "TRY REMOVE non-existent notification " + logKey(entry));
+ return false;
+ } else if (storedEntry != entry) {
throw mEulogizer.record(
- new IllegalStateException("No notification to remove with key "
- + logKey(entry)));
+ new IllegalStateException("Mismatched stored and tryRemoved entries"
+ + " for key " + logKey(entry) + ":"
+ + " stored=@" + Integer.toHexString(storedEntry.hashCode())
+ + " tryRemoved=@" + Integer.toHexString(entry.hashCode())));
}
if (!entry.isCanceled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
index 706594cee81d..2fa070ca20b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
@@ -28,9 +28,12 @@ import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.expansionChanges
+import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
@@ -47,29 +50,30 @@ import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
import java.io.PrintWriter
import javax.inject.Inject
+import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emitAll
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield
/**
* Filters low priority and privacy-sensitive notifications from the lockscreen, and hides section
- * headers on the lockscreen. If enabled, it will also track and hide seen notifications on the
- * lockscreen.
+ * headers on the lockscreen.
*/
@CoordinatorScope
class KeyguardCoordinator
@@ -82,6 +86,7 @@ constructor(
private val keyguardRepository: KeyguardRepository,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val logger: KeyguardCoordinatorLogger,
+ private val notifPipelineFlags: NotifPipelineFlags,
@Application private val scope: CoroutineScope,
private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider,
private val secureSettings: SecureSettings,
@@ -90,8 +95,6 @@ constructor(
) : Coordinator, Dumpable {
private val unseenNotifications = mutableSetOf<NotificationEntry>()
- private val unseenEntryAdded = MutableSharedFlow<NotificationEntry>(extraBufferCapacity = 1)
- private val unseenEntryRemoved = MutableSharedFlow<NotificationEntry>(extraBufferCapacity = 1)
private var unseenFilterEnabled = false
override fun attach(pipeline: NotifPipeline) {
@@ -106,131 +109,79 @@ constructor(
private fun attachUnseenFilter(pipeline: NotifPipeline) {
pipeline.addFinalizeFilter(unseenNotifFilter)
pipeline.addCollectionListener(collectionListener)
- scope.launch { trackSeenNotifications() }
+ scope.launch { trackUnseenNotificationsWhileUnlocked() }
scope.launch { invalidateWhenUnseenSettingChanges() }
dumpManager.registerDumpable(this)
}
- private suspend fun trackSeenNotifications() {
- // Whether or not keyguard is visible (or occluded).
- val isKeyguardPresent: Flow<Boolean> =
- keyguardTransitionRepository.transitions
- .map { step -> step.to != KeyguardState.GONE }
+ private suspend fun trackUnseenNotificationsWhileUnlocked() {
+ // Whether or not we're actively tracking unseen notifications to mark them as seen when
+ // appropriate.
+ val isTrackingUnseen: Flow<Boolean> =
+ keyguardRepository.isKeyguardShowing
+ // transformLatest so that we can cancel listening to keyguard transitions once
+ // isKeyguardShowing changes (after a successful transition to the keyguard).
+ .transformLatest { isShowing ->
+ if (isShowing) {
+ // If the keyguard is showing, we're not tracking unseen.
+ emit(false)
+ } else {
+ // If the keyguard stops showing, then start tracking unseen notifications.
+ emit(true)
+ // If the screen is turning off, stop tracking, but if that transition is
+ // cancelled, then start again.
+ emitAll(
+ keyguardTransitionRepository.transitions.map { step ->
+ !step.isScreenTurningOff
+ }
+ )
+ }
+ }
+ // Prevent double emit of `false` caused by transition to AOD, followed by keyguard
+ // showing
.distinctUntilChanged()
.onEach { trackingUnseen -> logger.logTrackingUnseen(trackingUnseen) }
- // Separately track seen notifications while the device is locked, applying once the device
- // is unlocked.
- val notificationsSeenWhileLocked = mutableSetOf<NotificationEntry>()
-
- // Use [collectLatest] to cancel any running jobs when [trackingUnseen] changes.
- isKeyguardPresent.collectLatest { isKeyguardPresent: Boolean ->
- if (isKeyguardPresent) {
- // Keyguard is not gone, notifications need to be visible for a certain threshold
- // before being marked as seen
- trackSeenNotificationsWhileLocked(notificationsSeenWhileLocked)
+ // Use collectLatest so that trackUnseenNotifications() is cancelled when the keyguard is
+ // showing again
+ var clearUnseenOnBeginTracking = false
+ isTrackingUnseen.collectLatest { trackingUnseen ->
+ if (!trackingUnseen) {
+ // Wait for the user to spend enough time on the lock screen before clearing unseen
+ // set when unlocked
+ awaitTimeSpentNotDozing(SEEN_TIMEOUT)
+ clearUnseenOnBeginTracking = true
+ logger.logSeenOnLockscreen()
} else {
- // Mark all seen-while-locked notifications as seen for real.
- if (notificationsSeenWhileLocked.isNotEmpty()) {
- unseenNotifications.removeAll(notificationsSeenWhileLocked)
- logger.logAllMarkedSeenOnUnlock(
- seenCount = notificationsSeenWhileLocked.size,
- remainingUnseenCount = unseenNotifications.size
- )
- notificationsSeenWhileLocked.clear()
+ if (clearUnseenOnBeginTracking) {
+ clearUnseenOnBeginTracking = false
+ logger.logAllMarkedSeenOnUnlock()
+ unseenNotifications.clear()
}
unseenNotifFilter.invalidateList("keyguard no longer showing")
- // Keyguard is gone, notifications can be immediately marked as seen when they
- // become visible.
- trackSeenNotificationsWhileUnlocked()
+ trackUnseenNotifications()
}
}
}
- /**
- * Keep [notificationsSeenWhileLocked] updated to represent which notifications have actually
- * been "seen" while the device is on the keyguard.
- */
- private suspend fun trackSeenNotificationsWhileLocked(
- notificationsSeenWhileLocked: MutableSet<NotificationEntry>,
- ) = coroutineScope {
- // Remove removed notifications from the set
- launch {
- unseenEntryRemoved.collect { entry ->
- if (notificationsSeenWhileLocked.remove(entry)) {
- logger.logRemoveSeenOnLockscreen(entry)
+ private suspend fun awaitTimeSpentNotDozing(duration: Duration) {
+ keyguardRepository.isDozing
+ // Use transformLatest so that the timeout delay is cancelled if the device enters doze,
+ // and is restarted when doze ends.
+ .transformLatest { isDozing ->
+ if (!isDozing) {
+ delay(duration)
+ // Signal timeout has completed
+ emit(Unit)
}
}
- }
- // Use collectLatest so that the timeout delay is cancelled if the device enters doze, and
- // is restarted when doze ends.
- keyguardRepository.isDozing.collectLatest { isDozing ->
- if (!isDozing) {
- trackSeenNotificationsWhileLockedAndNotDozing(notificationsSeenWhileLocked)
- }
- }
+ // Suspend until the first emission
+ .first()
}
- /**
- * Keep [notificationsSeenWhileLocked] updated to represent which notifications have actually
- * been "seen" while the device is on the keyguard and not dozing. Any new and existing unseen
- * notifications are not marked as seen until they are visible for the [SEEN_TIMEOUT] duration.
- */
- private suspend fun trackSeenNotificationsWhileLockedAndNotDozing(
- notificationsSeenWhileLocked: MutableSet<NotificationEntry>
- ) = coroutineScope {
- // All child tracking jobs will be cancelled automatically when this is cancelled.
- val trackingJobsByEntry = mutableMapOf<NotificationEntry, Job>()
-
- /**
- * Wait for the user to spend enough time on the lock screen before removing notification
- * from unseen set upon unlock.
- */
- suspend fun trackSeenDurationThreshold(entry: NotificationEntry) {
- if (notificationsSeenWhileLocked.remove(entry)) {
- logger.logResetSeenOnLockscreen(entry)
- }
- delay(SEEN_TIMEOUT)
- notificationsSeenWhileLocked.add(entry)
- trackingJobsByEntry.remove(entry)
- logger.logSeenOnLockscreen(entry)
- }
-
- /** Stop any unseen tracking when a notification is removed. */
- suspend fun stopTrackingRemovedNotifs(): Nothing =
- unseenEntryRemoved.collect { entry ->
- trackingJobsByEntry.remove(entry)?.let {
- it.cancel()
- logger.logStopTrackingLockscreenSeenDuration(entry)
- }
- }
-
- /** Start tracking new notifications when they are posted. */
- suspend fun trackNewUnseenNotifs(): Nothing = coroutineScope {
- unseenEntryAdded.collect { entry ->
- logger.logTrackingLockscreenSeenDuration(entry)
- // If this is an update, reset the tracking.
- trackingJobsByEntry[entry]?.let {
- it.cancel()
- logger.logResetSeenOnLockscreen(entry)
- }
- trackingJobsByEntry[entry] = launch { trackSeenDurationThreshold(entry) }
- }
- }
-
- // Start tracking for all notifications that are currently unseen.
- logger.logTrackingLockscreenSeenDuration(unseenNotifications)
- unseenNotifications.forEach { entry ->
- trackingJobsByEntry[entry] = launch { trackSeenDurationThreshold(entry) }
- }
-
- launch { trackNewUnseenNotifs() }
- launch { stopTrackingRemovedNotifs() }
- }
-
- // Track "seen" notifications, marking them as such when either shade is expanded or the
+ // Track "unseen" notifications, marking them as seen when either shade is expanded or the
// notification becomes heads up.
- private suspend fun trackSeenNotificationsWhileUnlocked() {
+ private suspend fun trackUnseenNotifications() {
coroutineScope {
launch { clearUnseenNotificationsWhenShadeIsExpanded() }
launch { markHeadsUpNotificationsAsSeen() }
@@ -299,7 +250,6 @@ constructor(
) {
logger.logUnseenAdded(entry.key)
unseenNotifications.add(entry)
- unseenEntryAdded.tryEmit(entry)
}
}
@@ -309,14 +259,12 @@ constructor(
) {
logger.logUnseenUpdated(entry.key)
unseenNotifications.add(entry)
- unseenEntryAdded.tryEmit(entry)
}
}
override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
if (unseenNotifications.remove(entry)) {
logger.logUnseenRemoved(entry.key)
- unseenEntryRemoved.tryEmit(entry)
}
}
}
@@ -399,3 +347,6 @@ constructor(
private val SEEN_TIMEOUT = 5.seconds
}
}
+
+private val TransitionStep.isScreenTurningOff: Boolean
+ get() = transitionState == TransitionState.STARTED && to != KeyguardState.GONE
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt
index c61281661717..1f8ec3411bcd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt
@@ -19,7 +19,6 @@ package com.android.systemui.statusbar.notification.collection.coordinator
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel
import com.android.systemui.log.dagger.UnseenNotificationLog
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
import javax.inject.Inject
private const val TAG = "KeyguardCoordinator"
@@ -29,14 +28,11 @@ class KeyguardCoordinatorLogger
constructor(
@UnseenNotificationLog private val buffer: LogBuffer,
) {
- fun logSeenOnLockscreen(entry: NotificationEntry) =
+ fun logSeenOnLockscreen() =
buffer.log(
TAG,
LogLevel.DEBUG,
- messageInitializer = { str1 = entry.key },
- messagePrinter = {
- "Notification [$str1] on lockscreen will be marked as seen when unlocked."
- },
+ "Notifications on lockscreen will be marked as seen when unlocked."
)
fun logTrackingUnseen(trackingUnseen: Boolean) =
@@ -47,21 +43,11 @@ constructor(
messagePrinter = { "${if (bool1) "Start" else "Stop"} tracking unseen notifications." },
)
- fun logAllMarkedSeenOnUnlock(
- seenCount: Int,
- remainingUnseenCount: Int,
- ) =
+ fun logAllMarkedSeenOnUnlock() =
buffer.log(
TAG,
LogLevel.DEBUG,
- messageInitializer = {
- int1 = seenCount
- int2 = remainingUnseenCount
- },
- messagePrinter = {
- "$int1 Notifications have been marked as seen now that device is unlocked. " +
- "$int2 notifications remain unseen."
- },
+ "Notifications have been marked as seen now that device is unlocked."
)
fun logShadeExpanded() =
@@ -110,60 +96,4 @@ constructor(
messageInitializer = { str1 = key },
messagePrinter = { "Unseen notif has become heads up: $str1" },
)
-
- fun logTrackingLockscreenSeenDuration(unseenNotifications: Set<NotificationEntry>) {
- buffer.log(
- TAG,
- LogLevel.DEBUG,
- messageInitializer = {
- str1 = unseenNotifications.joinToString { it.key }
- int1 = unseenNotifications.size
- },
- messagePrinter = {
- "Tracking $int1 unseen notifications for lockscreen seen duration threshold: $str1"
- },
- )
- }
-
- fun logTrackingLockscreenSeenDuration(entry: NotificationEntry) {
- buffer.log(
- TAG,
- LogLevel.DEBUG,
- messageInitializer = { str1 = entry.key },
- messagePrinter = {
- "Tracking new notification for lockscreen seen duration threshold: $str1"
- },
- )
- }
-
- fun logStopTrackingLockscreenSeenDuration(entry: NotificationEntry) {
- buffer.log(
- TAG,
- LogLevel.DEBUG,
- messageInitializer = { str1 = entry.key },
- messagePrinter = {
- "Stop tracking removed notification for lockscreen seen duration threshold: $str1"
- },
- )
- }
-
- fun logResetSeenOnLockscreen(entry: NotificationEntry) {
- buffer.log(
- TAG,
- LogLevel.DEBUG,
- messageInitializer = { str1 = entry.key },
- messagePrinter = {
- "Reset tracking updated notification for lockscreen seen duration threshold: $str1"
- },
- )
- }
-
- fun logRemoveSeenOnLockscreen(entry: NotificationEntry) {
- buffer.log(
- TAG,
- LogLevel.DEBUG,
- messageInitializer = { str1 = entry.key },
- messagePrinter = { "Notification marked as seen on lockscreen removed: $str1" },
- )
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index bdb206beb123..38c37239a7d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -71,7 +71,6 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
private NotificationPresenter mPresenter;
private NotificationListContainer mListContainer;
- private BindRowCallback mBindRowCallback;
private NotificationClicker mNotificationClicker;
private FeatureFlags mFeatureFlags;
@@ -103,11 +102,9 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
* Sets up late-bound dependencies for this component.
*/
public void setUpWithPresenter(NotificationPresenter presenter,
- NotificationListContainer listContainer,
- BindRowCallback bindRowCallback) {
+ NotificationListContainer listContainer) {
mPresenter = presenter;
mListContainer = listContainer;
- mBindRowCallback = bindRowCallback;
mIconManager.attach();
}
@@ -179,7 +176,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
mNotificationRemoteInputManager.bindRow(row);
entry.setRow(row);
mNotifBindPipeline.manageRow(entry, row);
- mBindRowCallback.onBindRow(row);
+ mPresenter.onBindRow(row);
row.setInlineReplyAnimationFlagEnabled(
mFeatureFlags.isEnabled(NOTIFICATION_INLINE_REPLY_ANIMATION));
}
@@ -235,12 +232,4 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
}
});
}
-
- /** Callback for when a row is bound to an entry. */
- public interface BindRowCallback {
- /**
- * Called when a new row is created and bound to a notification.
- */
- void onBindRow(ExpandableNotificationRow row);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
index a5278c3d0ad3..c9ebed18c623 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
@@ -20,7 +20,6 @@ import android.service.notification.StatusBarNotification
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
import com.android.systemui.statusbar.NotificationPresenter
import com.android.systemui.statusbar.notification.NotificationActivityStarter
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.collection.render.NotifStackController
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -38,7 +37,6 @@ interface NotificationsController {
listContainer: NotificationListContainer,
stackController: NotifStackController,
notificationActivityStarter: NotificationActivityStarter,
- bindRowCallback: NotificationRowBinderImpl.BindRowCallback
)
fun resetUserExpandedStates()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 0ed41758f215..6409635af250 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -83,7 +83,6 @@ class NotificationsControllerImpl @Inject constructor(
listContainer: NotificationListContainer,
stackController: NotifStackController,
notificationActivityStarter: NotificationActivityStarter,
- bindRowCallback: NotificationRowBinderImpl.BindRowCallback
) {
notificationListener.registerAsSystemService()
@@ -97,10 +96,7 @@ class NotificationsControllerImpl @Inject constructor(
clickerBuilder.build(
Optional.ofNullable(centralSurfaces), bubblesOptional,
notificationActivityStarter))
- notificationRowBinder.setUpWithPresenter(
- presenter,
- listContainer,
- bindRowCallback)
+ notificationRowBinder.setUpWithPresenter(presenter, listContainer)
headsUpViewBinder.setPresenter(presenter)
notifBindPipelineInitializer.initialize()
animatedImageNotificationManager.bind()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
index 14856dafdb11..302a1f422240 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
@@ -21,7 +21,6 @@ import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.Snoo
import com.android.systemui.statusbar.NotificationListener
import com.android.systemui.statusbar.NotificationPresenter
import com.android.systemui.statusbar.notification.NotificationActivityStarter
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.collection.render.NotifStackController
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -40,7 +39,6 @@ class NotificationsControllerStub @Inject constructor(
listContainer: NotificationListContainer,
stackController: NotifStackController,
notificationActivityStarter: NotificationActivityStarter,
- bindRowCallback: NotificationRowBinderImpl.BindRowCallback
) {
// Always connect the listener even if notification-handling is disabled. Being a listener
// grants special permissions and it's not clear if other things will break if we lose those
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
index 014406fe49f9..69484b06c776 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
@@ -17,19 +17,19 @@
package com.android.systemui.statusbar.notification.shelf.domain.interactor
import android.os.PowerManager
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.phone.CentralSurfaces
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
/** Interactor for the [NotificationShelf] */
-@CentralSurfacesComponent.CentralSurfacesScope
+@SysUISingleton
class NotificationShelfInteractor
@Inject
constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
index 12956ab9498a..252073825bce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.shelf.ui.viewbinder
import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -32,7 +33,6 @@ import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.NotificationIconAreaController
import com.android.systemui.statusbar.phone.NotificationIconContainer
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
import javax.inject.Inject
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.launch
@@ -43,7 +43,7 @@ import kotlinx.coroutines.launch
* [NotificationShelfController] interface. Once the [LegacyNotificationShelfControllerImpl] is
* removed, this class can go away and the ViewBinder can be used directly.
*/
-@CentralSurfacesScope
+@SysUISingleton
class NotificationShelfViewBinderWrapperControllerImpl @Inject constructor() :
NotificationShelfController {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index a70862aead0f..aef623ae8fd0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -56,11 +56,13 @@ import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.ExpandHelper;
import com.android.systemui.Gefingerpoken;
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.media.controls.ui.KeyguardMediaController;
import com.android.systemui.plugins.ActivityStarter;
@@ -175,6 +177,7 @@ public class NotificationStackScrollLayoutController {
private final SysuiStatusBarStateController mStatusBarStateController;
private final KeyguardBypassController mKeyguardBypassController;
private final KeyguardInteractor mKeyguardInteractor;
+ private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
private final NotificationLockscreenUserManager mLockscreenUserManager;
// TODO: CentralSurfaces should be encapsulated behind a Controller
private final CentralSurfaces mCentralSurfaces;
@@ -195,6 +198,7 @@ public class NotificationStackScrollLayoutController {
@Nullable
private Boolean mHistoryEnabled;
private int mBarState;
+ private boolean mIsBouncerShowingFromCentralSurfaces;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private final FeatureFlags mFeatureFlags;
private final NotificationTargetsHelper mNotificationTargetsHelper;
@@ -631,6 +635,7 @@ public class NotificationStackScrollLayoutController {
KeyguardMediaController keyguardMediaController,
KeyguardBypassController keyguardBypassController,
KeyguardInteractor keyguardInteractor,
+ PrimaryBouncerInteractor primaryBouncerInteractor,
ZenModeController zenModeController,
NotificationLockscreenUserManager lockscreenUserManager,
Optional<NotificationListViewModel> nsslViewModel,
@@ -680,6 +685,7 @@ public class NotificationStackScrollLayoutController {
mKeyguardMediaController = keyguardMediaController;
mKeyguardBypassController = keyguardBypassController;
mKeyguardInteractor = keyguardInteractor;
+ mPrimaryBouncerInteractor = primaryBouncerInteractor;
mZenModeController = zenModeController;
mLockscreenUserManager = lockscreenUserManager;
mViewModel = nsslViewModel;
@@ -1206,6 +1212,14 @@ public class NotificationStackScrollLayoutController {
}
/**
+ * Sets whether the bouncer is currently showing. Should only be called from
+ * {@link CentralSurfaces}.
+ */
+ public void setBouncerShowingFromCentralSurfaces(boolean bouncerShowing) {
+ mIsBouncerShowingFromCentralSurfaces = bouncerShowing;
+ }
+
+ /**
* Set the visibility of the view, and propagate it to specific children.
*
* @param visible either the view is visible or not.
@@ -1236,7 +1250,8 @@ public class NotificationStackScrollLayoutController {
// That avoids "No Notifications" to blink when transitioning to AOD.
// For more details, see: b/228790482
&& !isInTransitionToKeyguard()
- && !mCentralSurfaces.isBouncerShowing();
+ // Don't show any notification content if the bouncer is showing. See b/267060171.
+ && !isBouncerShowing();
mView.updateEmptyShadeView(shouldShow, mZenModeController.areNotificationsHiddenInShade());
@@ -1244,6 +1259,24 @@ public class NotificationStackScrollLayoutController {
}
/**
+ * Returns whether the bouncer is currently showing.
+ *
+ * There's a possible timing difference between when CentralSurfaces marks the bouncer as not
+ * showing and when PrimaryBouncerInteractor marks the bouncer as not showing. (CentralSurfaces
+ * appears to mark the bouncer as showing for 10-200ms longer than PrimaryBouncerInteractor.)
+ *
+ * This timing difference could be load bearing, which is why we have a feature flag protecting
+ * where we fetch the value from. This flag is intended to be short-lived.
+ */
+ private boolean isBouncerShowing() {
+ if (mFeatureFlags.isEnabled(Flags.USE_REPOS_FOR_BOUNCER_SHOWING)) {
+ return mPrimaryBouncerInteractor.isBouncerShowing();
+ } else {
+ return mIsBouncerShowingFromCentralSurfaces;
+ }
+ }
+
+ /**
* Update the importantForAccessibility of NotificationStackScrollLayout.
* <p>
* We want the NSSL to be unimportant for accessibility when there's no
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 81048d60c6d9..1396d8efc7af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -173,6 +173,7 @@ import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.recents.ScreenPinningRequest;
@@ -634,6 +635,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private final SysuiColorExtractor mColorExtractor;
private final ScreenLifecycle mScreenLifecycle;
private final WakefulnessLifecycle mWakefulnessLifecycle;
+ protected final PowerInteractor mPowerInteractor;
private boolean mNoAnimationOnNextBarModeChange;
private final SysuiStatusBarStateController mStatusBarStateController;
@@ -648,7 +650,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private final Optional<StartingSurface> mStartingSurfaceOptional;
private final ActivityIntentHelper mActivityIntentHelper;
- private NotificationStackScrollLayoutController mStackScrollerController;
+
+ @VisibleForTesting
+ protected NotificationStackScrollLayoutController mStackScrollerController;
private final ColorExtractor.OnColorsChangedListener mOnColorsChangedListener =
(extractor, which) -> updateTheme();
@@ -735,6 +739,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
SysuiColorExtractor colorExtractor,
ScreenLifecycle screenLifecycle,
WakefulnessLifecycle wakefulnessLifecycle,
+ PowerInteractor powerInteractor,
SysuiStatusBarStateController statusBarStateController,
Optional<Bubbles> bubblesOptional,
Lazy<NoteTaskController> noteTaskControllerLazy,
@@ -744,6 +749,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
Lazy<AssistManager> assistManagerLazy,
ConfigurationController configurationController,
NotificationShadeWindowController notificationShadeWindowController,
+ NotificationShelfController notificationShelfController,
DozeParameters dozeParameters,
ScrimController scrimController,
Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
@@ -831,6 +837,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mColorExtractor = colorExtractor;
mScreenLifecycle = screenLifecycle;
mWakefulnessLifecycle = wakefulnessLifecycle;
+ mPowerInteractor = powerInteractor;
mStatusBarStateController = statusBarStateController;
mBubblesOptional = bubblesOptional;
mNoteTaskControllerLazy = noteTaskControllerLazy;
@@ -840,6 +847,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mAssistManagerLazy = assistManagerLazy;
mConfigurationController = configurationController;
mNotificationShadeWindowController = notificationShadeWindowController;
+ mNotificationShelfController = notificationShelfController;
mDozeServiceHost = dozeServiceHost;
mPowerManager = powerManager;
mDozeParameters = dozeParameters;
@@ -1571,8 +1579,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mPresenter,
mNotifListContainer,
mStackScrollerController.getNotifStackController(),
- mNotificationActivityStarter,
- mCentralSurfacesComponent.getBindRowCallback());
+ mNotificationActivityStarter);
}
/**
@@ -1651,7 +1658,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mNotifListContainer = mCentralSurfacesComponent.getNotificationListContainer();
mPresenter = mCentralSurfacesComponent.getNotificationPresenter();
mNotificationActivityStarter = mCentralSurfacesComponent.getNotificationActivityStarter();
- mNotificationShelfController = mCentralSurfacesComponent.getNotificationShelfController();
mHeadsUpManager.addListener(mCentralSurfacesComponent.getStatusBarHeadsUpChangeListener());
@@ -2922,6 +2928,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mBouncerShowing = bouncerShowing;
mKeyguardBypassController.setBouncerShowing(bouncerShowing);
mPulseExpansionHandler.setBouncerShowing(bouncerShowing);
+ mStackScrollerController.setBouncerShowingFromCentralSurfaces(bouncerShowing);
setBouncerShowingForStatusBarComponents(bouncerShowing);
mStatusBarHideIconsForBouncerManager.setBouncerShowingAndTriggerUpdate(bouncerShowing);
mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
@@ -3408,7 +3415,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
protected Display mDisplay;
private int mDisplayId;
- protected NotificationShelfController mNotificationShelfController;
+ private final NotificationShelfController mNotificationShelfController;
private final Lazy<AssistManager> mAssistManagerLazy;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 561bd91b964f..fc3c85a0807b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -229,12 +229,18 @@ public class KeyguardClockPositionAlgorithm {
}
}
- public float getLockscreenMinStackScrollerPadding() {
+ /**
+ * @param nsslTop NotificationStackScrollLayout top, which is below top of the srceen.
+ * @return Distance from nsslTop to top of the first view in the lockscreen shade.
+ */
+ public float getLockscreenNotifPadding(float nsslTop) {
if (mBypassEnabled) {
- return mUnlockedStackScrollerPadding;
+ return mUnlockedStackScrollerPadding - nsslTop;
} else if (mIsSplitShade) {
- return mSplitShadeTargetTopMargin + mUserSwitchHeight;
+ return mSplitShadeTargetTopMargin + mUserSwitchHeight - nsslTop;
} else {
+ // Non-bypass portrait shade already uses values from nsslTop
+ // so we don't need to subtract it here.
return mMinTopMargin + mKeyguardStatusHeight;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 5624e28204cc..3b56d2769d7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -52,7 +52,6 @@ import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
@@ -68,9 +67,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import javax.inject.Inject;
@CentralSurfacesComponent.CentralSurfacesScope
-class StatusBarNotificationPresenter implements NotificationPresenter,
- NotificationRowBinderImpl.BindRowCallback,
- CommandQueue.Callbacks {
+class StatusBarNotificationPresenter implements NotificationPresenter, CommandQueue.Callbacks {
private static final String TAG = "StatusBarNotificationPresenter";
private final ActivityStarter mActivityStarter;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterModule.java
index 26c483ce18fa..d94e434c3339 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterModule.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.phone;
import com.android.systemui.statusbar.NotificationPresenter;
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import dagger.Binds;
import dagger.Module;
@@ -26,8 +25,4 @@ import dagger.Module;
public abstract class StatusBarNotificationPresenterModule {
@Binds
abstract NotificationPresenter bindPresenter(StatusBarNotificationPresenter impl);
-
- @Binds
- abstract NotificationRowBinderImpl.BindRowCallback bindBindRowCallback(
- StatusBarNotificationPresenter impl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
index 158f96123d92..64c798b99a18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
@@ -27,9 +27,7 @@ import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.shade.QuickSettingsController;
import com.android.systemui.shade.ShadeHeaderController;
import com.android.systemui.statusbar.NotificationPresenter;
-import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutListContainerModule;
@@ -90,9 +88,6 @@ public interface CentralSurfacesComponent {
NotificationShadeWindowView getNotificationShadeWindowView();
/** */
- NotificationShelfController getNotificationShelfController();
-
- /** */
NotificationStackScrollLayoutController getNotificationStackScrollLayoutController();
/**
@@ -134,7 +129,5 @@ public interface CentralSurfacesComponent {
NotificationPresenter getNotificationPresenter();
- NotificationRowBinderImpl.BindRowCallback getBindRowCallback();
-
NotificationListContainer getNotificationListContainer();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 76409c26be8f..260d986db6c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -23,22 +23,15 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.NotificationPanelView;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.LegacyNotificationShelfControllerImpl;
-import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
-import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule;
-import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModelModule;
import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
@@ -66,7 +59,6 @@ import dagger.multibindings.IntoSet;
import java.util.concurrent.Executor;
import javax.inject.Named;
-import javax.inject.Provider;
@Module(subcomponents = StatusBarFragmentComponent.class,
includes = {
@@ -78,43 +70,6 @@ public abstract class StatusBarViewModule {
public static final String STATUS_BAR_FRAGMENT = "status_bar_fragment";
/** */
- @Provides
- @CentralSurfacesComponent.CentralSurfacesScope
- public static NotificationShelf providesNotificationShelf(LayoutInflater layoutInflater,
- NotificationStackScrollLayout notificationStackScrollLayout) {
- NotificationShelf view = (NotificationShelf) layoutInflater.inflate(
- R.layout.status_bar_notification_shelf, notificationStackScrollLayout, false);
-
- if (view == null) {
- throw new IllegalStateException(
- "R.layout.status_bar_notification_shelf could not be properly inflated");
- }
- return view;
- }
-
- /** */
- @Provides
- @CentralSurfacesComponent.CentralSurfacesScope
- public static NotificationShelfController providesStatusBarWindowView(
- FeatureFlags featureFlags,
- Provider<NotificationShelfViewBinderWrapperControllerImpl> newImpl,
- NotificationShelfComponent.Builder notificationShelfComponentBuilder,
- NotificationShelf notificationShelf) {
- if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
- return newImpl.get();
- } else {
- NotificationShelfComponent component = notificationShelfComponentBuilder
- .notificationShelf(notificationShelf)
- .build();
- LegacyNotificationShelfControllerImpl notificationShelfController =
- component.getNotificationShelfController();
- notificationShelfController.init();
-
- return notificationShelfController;
- }
- }
-
- /** */
@Binds
@CentralSurfacesComponent.CentralSurfacesScope
abstract ShadeViewController bindsShadeViewController(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index f37a9b5842a7..7456d349a933 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -928,7 +928,6 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
showRingerDrawer();
}
});
- updateSelectedRingerContainerDescription(mIsRingerDrawerOpen);
mRingerDrawerVibrate.setOnClickListener(
new RingerDrawerItemClickListener(RINGER_MODE_VIBRATE));
@@ -991,19 +990,6 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
: 0;
}
- @VisibleForTesting String getSelectedRingerContainerDescription() {
- return mSelectedRingerContainer == null ? null :
- mSelectedRingerContainer.getContentDescription().toString();
- }
-
- @VisibleForTesting void toggleRingerDrawer(boolean show) {
- if (show) {
- showRingerDrawer();
- } else {
- hideRingerDrawer();
- }
- }
-
/** Animates in the ringer drawer. */
private void showRingerDrawer() {
if (mIsRingerDrawerOpen) {
@@ -1081,7 +1067,12 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
.start();
}
- updateSelectedRingerContainerDescription(true);
+ // When the ringer drawer is open, tapping the currently selected ringer will set the ringer
+ // to the current ringer mode. Change the content description to that, instead of the 'tap
+ // to change ringer mode' default.
+ mSelectedRingerContainer.setContentDescription(
+ mContext.getString(getStringDescriptionResourceForRingerMode(
+ mState.ringerModeInternal)));
mIsRingerDrawerOpen = true;
}
@@ -1127,38 +1118,14 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
.translationY(0f)
.start();
- updateSelectedRingerContainerDescription(false);
+ // When the drawer is closed, tapping the selected ringer drawer will open it, allowing the
+ // user to change the ringer.
+ mSelectedRingerContainer.setContentDescription(
+ mContext.getString(R.string.volume_ringer_change));
mIsRingerDrawerOpen = false;
}
-
- /**
- * @param open false to set the description when drawer is closed
- */
- private void updateSelectedRingerContainerDescription(boolean open) {
- if (mState == null || mSelectedRingerContainer == null) return;
-
- String currentMode = mContext.getString(getStringDescriptionResourceForRingerMode(
- mState.ringerModeInternal));
- String tapToSelect;
-
- if (open) {
- // When the ringer drawer is open, tapping the currently selected ringer will set the
- // ringer to the current ringer mode. Change the content description to that, instead of
- // the 'tap to change ringer mode' default.
- tapToSelect = "";
-
- } else {
- // When the drawer is closed, tapping the selected ringer drawer will open it, allowing
- // the user to change the ringer. The user needs to know that, and also the current mode
- currentMode += ", ";
- tapToSelect = mContext.getString(R.string.volume_ringer_change);
- }
-
- mSelectedRingerContainer.setContentDescription(currentMode + tapToSelect);
- }
-
private void initSettingsH(int lockTaskModeState) {
if (mSettingsView != null) {
mSettingsView.setVisibility(
@@ -1734,7 +1701,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
});
}
- @VisibleForTesting int getStringDescriptionResourceForRingerMode(int mode) {
+ private int getStringDescriptionResourceForRingerMode(int mode) {
switch (mode) {
case RINGER_MODE_SILENT:
return R.string.volume_ringer_status_silent;
@@ -1816,7 +1783,6 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
updateVolumeRowH(row);
}
updateRingerH();
- updateSelectedRingerContainerDescription(mIsRingerDrawerOpen);
mWindow.setTitle(composeWindowTitle());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
index 292fdff0027d..f770a3885aad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
@@ -187,4 +187,11 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
when(mFalsingDataProvider.isUnfolded()).thenReturn(true);
assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
}
+
+ @Test
+ public void testTrackpadGesture() {
+ assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue();
+ when(mFalsingDataProvider.isFromTrackpad()).thenReturn(true);
+ assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java
index 925461765546..4da151c3ca04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java
@@ -18,8 +18,11 @@ package com.android.systemui.classifier;
import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
import android.util.DisplayMetrics;
+import android.view.InputDevice;
import android.view.MotionEvent;
+import androidx.test.uiautomator.Configurator;
+
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManagerFake;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -80,6 +83,10 @@ public class ClassifierTest extends SysuiTestCase {
mDataProvider.onSessionEnd();
}
+ protected static int getPointerAction(int actionType, int index) {
+ return actionType + (index << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
+ }
+
protected MotionEvent appendDownEvent(float x, float y) {
return appendMotionEvent(MotionEvent.ACTION_DOWN, x, y);
}
@@ -124,4 +131,55 @@ public class ClassifierTest extends SysuiTestCase {
return motionEvent;
}
+
+ protected MotionEvent appendTrackpadDownEvent(float x, float y) {
+ return appendTrackpadMotionEvent(MotionEvent.ACTION_DOWN, x, y, 1);
+ }
+
+ protected MotionEvent appendTrackpadMoveEvent(float x, float y, int pointerCount) {
+ return appendTrackpadMotionEvent(MotionEvent.ACTION_MOVE, x, y, pointerCount);
+ }
+
+ protected MotionEvent appendTrackpadPointerDownEvent(int actionType, float x, float y,
+ int pointerCount) {
+ return appendTrackpadMotionEvent(actionType, x, y, pointerCount);
+ }
+
+ private MotionEvent appendTrackpadMotionEvent(int actionType, float x, float y,
+ int pointerCount) {
+ long eventTime = mMotionEvents.isEmpty() ? 1 : mMotionEvents.get(
+ mMotionEvents.size() - 1).getEventTime() + 1;
+ return appendTrackpadMotionEvent(actionType, x, y, pointerCount, eventTime);
+ }
+
+ private MotionEvent appendTrackpadMotionEvent(int actionType, float x, float y,
+ int pointerCount, long eventTime) {
+ MotionEvent.PointerProperties[] pointerProperties =
+ new MotionEvent.PointerProperties[pointerCount];
+ MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[pointerCount];
+ for (int i = 0; i < pointerCount; i++) {
+ pointerProperties[i] = getPointerProperties(i);
+ pointerCoords[i] = getPointerCoords(x, y);
+ }
+ return MotionEvent.obtain(1, eventTime, actionType, pointerCount, pointerProperties,
+ pointerCoords, 0, 0, 1.0f, 1.0f, 0, 0,
+ InputDevice.SOURCE_TOUCHPAD | InputDevice.SOURCE_MOUSE, 0, 0,
+ MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE);
+ }
+
+ private static MotionEvent.PointerProperties getPointerProperties(int pointerId) {
+ MotionEvent.PointerProperties properties = new MotionEvent.PointerProperties();
+ properties.id = pointerId;
+ properties.toolType = Configurator.getInstance().getToolType();
+ return properties;
+ }
+
+ private static MotionEvent.PointerCoords getPointerCoords(float x, float y) {
+ MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
+ coords.pressure = 1;
+ coords.size = 1;
+ coords.x = x;
+ coords.y = y;
+ return coords;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
index 7e066808b5bd..0353f87ae9b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
@@ -282,6 +282,22 @@ public class FalsingDataProviderTest extends ClassifierTest {
}
@Test
+ public void test_IsFromTrackpad() {
+ MotionEvent motionEventOrigin = appendTrackpadDownEvent(0, 0);
+
+ mDataProvider.onMotionEvent(motionEventOrigin);
+ mDataProvider.onMotionEvent(
+ appendTrackpadPointerDownEvent(getPointerAction(MotionEvent.ACTION_POINTER_DOWN, 1),
+ 0, 0, 2));
+ mDataProvider.onMotionEvent(
+ appendTrackpadPointerDownEvent(getPointerAction(MotionEvent.ACTION_POINTER_DOWN, 2),
+ 0, 0, 3));
+ mDataProvider.onMotionEvent(appendTrackpadMoveEvent(1, -1, 3));
+ assertThat(mDataProvider.isFromTrackpad()).isTrue();
+ mDataProvider.onSessionEnd();
+ }
+
+ @Test
public void test_isWirelessCharging() {
assertThat(mDataProvider.isDocked()).isFalse();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 6d8c9b106881..7df54d44e69e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -280,6 +280,50 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
}
@Test
+ public void onBindViewHolder_bindConnectedRemoteDeviceWithOnGoingSession_verifyView() {
+ when(mMediaDevice1.hasOngoingSession()).thenReturn(true);
+ when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(
+ ImmutableList.of());
+ when(mMediaOutputController.isCurrentConnectedDeviceRemote()).thenReturn(true);
+ mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
+ .onCreateViewHolder(new LinearLayout(mContext), 0);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1);
+ assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onBindViewHolder_bindConnectedRemoteDeviceWithHostOnGoingSession_verifyView() {
+ when(mMediaDevice1.hasOngoingSession()).thenReturn(true);
+ when(mMediaDevice1.isHostForOngoingSession()).thenReturn(true);
+ when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(
+ ImmutableList.of());
+ when(mMediaOutputController.isCurrentConnectedDeviceRemote()).thenReturn(true);
+ mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
+ .onCreateViewHolder(new LinearLayout(mContext), 0);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1);
+ assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mEndClickIcon.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
public void onBindViewHolder_bindConnectedDeviceWithMutingExpectedDeviceExist_verifyView() {
when(mMediaOutputController.hasMutingExpectedDevice()).thenReturn(true);
when(mMediaOutputController.isCurrentConnectedDeviceRemote()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
index 9dba9b5b3c3e..f8971fd7c99c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
@@ -136,7 +136,10 @@ public class MediaOutputBroadcastDialogTest extends SysuiTestCase {
@After
public void tearDown() {
- mMediaOutputBroadcastDialog.dismissDialog();
+ if (mMediaOutputBroadcastDialog.mAlertDialog != null){
+ mMediaOutputBroadcastDialog.mAlertDialog.dismiss();
+ }
+ mMediaOutputBroadcastDialog.dismiss();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperServiceTest.java
index ab321f1b5341..ba3d39248762 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperServiceTest.java
@@ -26,7 +26,7 @@ import android.hardware.HardwareBuffer;
import android.os.RemoteException;
import android.view.Display;
import android.window.ScreenCapture.ScreenshotHardwareBuffer;
-import android.window.ScreenCapture.ScreenshotSync;
+import android.window.ScreenCapture.SynchronousScreenCaptureListener;
import androidx.test.runner.AndroidJUnit4;
@@ -58,7 +58,7 @@ public final class AppClipsScreenshotHelperServiceTest extends SysuiTestCase {
@Mock private Optional<Bubbles> mBubblesOptional;
@Mock private Bubbles mBubbles;
@Mock private ScreenshotHardwareBuffer mScreenshotHardwareBuffer;
- @Mock private ScreenshotSync mScreenshotSync;
+ @Mock private SynchronousScreenCaptureListener mScreenshotSync;
private AppClipsScreenshotHelperService mAppClipsScreenshotHelperService;
@@ -80,7 +80,7 @@ public final class AppClipsScreenshotHelperServiceTest extends SysuiTestCase {
when(mBubblesOptional.isEmpty()).thenReturn(false);
when(mBubblesOptional.get()).thenReturn(mBubbles);
when(mBubbles.getScreenshotExcludingBubble(DEFAULT_DISPLAY)).thenReturn(mScreenshotSync);
- when(mScreenshotSync.get()).thenReturn(null);
+ when(mScreenshotSync.getBuffer()).thenReturn(null);
assertThat(getInterface().takeScreenshot(DEFAULT_DISPLAY)).isNull();
}
@@ -90,7 +90,7 @@ public final class AppClipsScreenshotHelperServiceTest extends SysuiTestCase {
when(mBubblesOptional.isEmpty()).thenReturn(false);
when(mBubblesOptional.get()).thenReturn(mBubbles);
when(mBubbles.getScreenshotExcludingBubble(DEFAULT_DISPLAY)).thenReturn(mScreenshotSync);
- when(mScreenshotSync.get()).thenReturn(mScreenshotHardwareBuffer);
+ when(mScreenshotSync.getBuffer()).thenReturn(mScreenshotHardwareBuffer);
when(mScreenshotHardwareBuffer.getHardwareBuffer()).thenReturn(FAKE_HARDWARE_BUFFER);
when(mScreenshotHardwareBuffer.getColorSpace()).thenReturn(FAKE_COLOR_SPACE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
new file mode 100644
index 000000000000..8bb8f6247e25
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2023 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.shade
+
+import android.testing.AndroidTestingRunner
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.QSFragment
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class NotificationsQuickSettingsContainerTest : SysuiTestCase() {
+
+ @Mock private lateinit var qsFrame: View
+ @Mock private lateinit var stackScroller: View
+ @Mock private lateinit var keyguardStatusBar: View
+ @Mock private lateinit var qsFragment: QSFragment
+
+ private lateinit var qsView: ViewGroup
+ private lateinit var qsContainer: View
+
+ private lateinit var underTest: NotificationsQuickSettingsContainer
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest = NotificationsQuickSettingsContainer(context, null)
+
+ setUpViews()
+ underTest.onFinishInflate()
+ underTest.onFragmentViewCreated("QS", qsFragment)
+ }
+
+ @Test
+ fun qsContainerPaddingSetAgainAfterQsRecreated() {
+ val padding = 100
+ underTest.setQSContainerPaddingBottom(padding)
+
+ assertThat(qsContainer.paddingBottom).isEqualTo(padding)
+
+ // We reset the padding before "creating" a new QSFragment
+ qsContainer.setPadding(0, 0, 0, 0)
+ underTest.onFragmentViewCreated("QS", qsFragment)
+
+ assertThat(qsContainer.paddingBottom).isEqualTo(padding)
+ }
+
+ private fun setUpViews() {
+ qsView = FrameLayout(context)
+ qsContainer = View(context)
+ qsContainer.id = R.id.quick_settings_container
+ qsView.addView(qsContainer)
+
+ whenever(qsFrame.findViewById<View>(R.id.qs_frame)).thenReturn(qsFrame)
+ whenever(stackScroller.findViewById<View>(R.id.notification_stack_scroller))
+ .thenReturn(stackScroller)
+ whenever(keyguardStatusBar.findViewById<View>(R.id.keyguard_header))
+ .thenReturn(keyguardStatusBar)
+ whenever(qsFragment.view).thenReturn(qsView)
+
+ val layoutParams = ConstraintLayout.LayoutParams(0, 0)
+ whenever(qsFrame.layoutParams).thenReturn(layoutParams)
+ whenever(stackScroller.layoutParams).thenReturn(layoutParams)
+ whenever(keyguardStatusBar.layoutParams).thenReturn(layoutParams)
+
+ underTest.addView(qsFrame)
+ underTest.addView(stackScroller)
+ underTest.addView(keyguardStatusBar)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
index f1d56f9e3480..a4fab1dbac57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
@@ -54,8 +54,6 @@ import org.mockito.Mockito.`when` as whenever
@SmallTest
class PulsingGestureListenerTest : SysuiTestCase() {
@Mock
- private lateinit var view: NotificationShadeWindowView
- @Mock
private lateinit var dockManager: DockManager
@Mock
private lateinit var falsingManager: FalsingManager
@@ -87,7 +85,6 @@ class PulsingGestureListenerTest : SysuiTestCase() {
powerRepository = FakePowerRepository()
underTest = PulsingGestureListener(
- view,
falsingManager,
dockManager,
PowerInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
index ea70e9e44c66..2fbe87158eba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
@@ -32,6 +32,7 @@ import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
@@ -45,14 +46,11 @@ import com.android.systemui.statusbar.notification.interruption.KeyguardNotifica
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
-import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
-import java.util.function.Consumer
-import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineScheduler
@@ -64,8 +62,9 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.same
import org.mockito.Mockito.anyString
import org.mockito.Mockito.clearInvocations
-import org.mockito.Mockito.never
import org.mockito.Mockito.verify
+import java.util.function.Consumer
+import kotlin.time.Duration.Companion.seconds
import org.mockito.Mockito.`when` as whenever
@SmallTest
@@ -76,6 +75,7 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
private val keyguardNotifVisibilityProvider: KeyguardNotificationVisibilityProvider = mock()
private val keyguardRepository = FakeKeyguardRepository()
private val keyguardTransitionRepository = FakeKeyguardTransitionRepository()
+ private val notifPipelineFlags: NotifPipelineFlags = mock()
private val notifPipeline: NotifPipeline = mock()
private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider = mock()
private val statusBarStateController: StatusBarStateController = mock()
@@ -136,8 +136,13 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
)
testScheduler.runCurrent()
- // THEN: We are no longer listening for shade expansions
- verify(statusBarStateController, never()).addCallback(any())
+ // WHEN: The shade is expanded
+ whenever(statusBarStateController.isExpanded).thenReturn(true)
+ statusBarStateListener.onExpandedChanged(true)
+ testScheduler.runCurrent()
+
+ // THEN: The notification is still treated as "unseen" and is not filtered out.
+ assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isFalse()
}
}
@@ -147,10 +152,6 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
keyguardRepository.setKeyguardShowing(false)
whenever(statusBarStateController.isExpanded).thenReturn(false)
runKeyguardCoordinatorTest {
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE)
- )
-
// WHEN: A notification is posted
val fakeEntry = NotificationEntryBuilder().build()
collectionListener.onEntryAdded(fakeEntry)
@@ -161,9 +162,6 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
// WHEN: The keyguard is now showing
keyguardRepository.setKeyguardShowing(true)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.AOD)
- )
testScheduler.runCurrent()
// THEN: The notification is recognized as "seen" and is filtered out.
@@ -171,9 +169,6 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
// WHEN: The keyguard goes away
keyguardRepository.setKeyguardShowing(false)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.AOD, to = KeyguardState.GONE)
- )
testScheduler.runCurrent()
// THEN: The notification is shown regardless
@@ -187,10 +182,9 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
keyguardRepository.setKeyguardShowing(false)
whenever(statusBarStateController.isExpanded).thenReturn(true)
runKeyguardCoordinatorTest {
- val fakeEntry =
- NotificationEntryBuilder()
+ val fakeEntry = NotificationEntryBuilder()
.setNotification(Notification.Builder(mContext, "id").setOngoing(true).build())
- .build()
+ .build()
collectionListener.onEntryAdded(fakeEntry)
// WHEN: The keyguard is now showing
@@ -208,13 +202,11 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
keyguardRepository.setKeyguardShowing(false)
whenever(statusBarStateController.isExpanded).thenReturn(true)
runKeyguardCoordinatorTest {
- val fakeEntry =
- NotificationEntryBuilder().build().apply {
- row =
- mock<ExpandableNotificationRow>().apply {
- whenever(isMediaRow).thenReturn(true)
- }
+ val fakeEntry = NotificationEntryBuilder().build().apply {
+ row = mock<ExpandableNotificationRow>().apply {
+ whenever(isMediaRow).thenReturn(true)
}
+ }
collectionListener.onEntryAdded(fakeEntry)
// WHEN: The keyguard is now showing
@@ -307,12 +299,14 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
runKeyguardCoordinatorTest {
// WHEN: A new notification is posted
val fakeSummary = NotificationEntryBuilder().build()
- val fakeChild =
- NotificationEntryBuilder()
+ val fakeChild = NotificationEntryBuilder()
.setGroup(context, "group")
.setGroupSummary(context, false)
.build()
- GroupEntryBuilder().setSummary(fakeSummary).addChild(fakeChild).build()
+ GroupEntryBuilder()
+ .setSummary(fakeSummary)
+ .addChild(fakeChild)
+ .build()
collectionListener.onEntryAdded(fakeSummary)
collectionListener.onEntryAdded(fakeChild)
@@ -337,10 +331,6 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
runKeyguardCoordinatorTest {
val fakeEntry = NotificationEntryBuilder().build()
collectionListener.onEntryAdded(fakeEntry)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.AOD, to = KeyguardState.LOCKSCREEN)
- )
- testScheduler.runCurrent()
// WHEN: five seconds have passed
testScheduler.advanceTimeBy(5.seconds)
@@ -348,16 +338,10 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
// WHEN: Keyguard is no longer showing
keyguardRepository.setKeyguardShowing(false)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE)
- )
testScheduler.runCurrent()
// WHEN: Keyguard is shown again
keyguardRepository.setKeyguardShowing(true)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.AOD)
- )
testScheduler.runCurrent()
// THEN: The notification is now recognized as "seen" and is filtered out.
@@ -370,17 +354,11 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
// GIVEN: Keyguard is showing, unseen notification is present
keyguardRepository.setKeyguardShowing(true)
runKeyguardCoordinatorTest {
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.LOCKSCREEN)
- )
val fakeEntry = NotificationEntryBuilder().build()
collectionListener.onEntryAdded(fakeEntry)
// WHEN: Keyguard is no longer showing
keyguardRepository.setKeyguardShowing(false)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE)
- )
// WHEN: Keyguard is shown again
keyguardRepository.setKeyguardShowing(true)
@@ -391,212 +369,14 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
}
}
- @Test
- fun unseenNotificationIsNotMarkedAsSeenIfNotOnKeyguardLongEnough() {
- // GIVEN: Keyguard is showing, not dozing, unseen notification is present
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setIsDozing(false)
- runKeyguardCoordinatorTest {
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.LOCKSCREEN)
- )
- val firstEntry = NotificationEntryBuilder().setId(1).build()
- collectionListener.onEntryAdded(firstEntry)
- testScheduler.runCurrent()
-
- // WHEN: one second has passed
- testScheduler.advanceTimeBy(1.seconds)
- testScheduler.runCurrent()
-
- // WHEN: another unseen notification is posted
- val secondEntry = NotificationEntryBuilder().setId(2).build()
- collectionListener.onEntryAdded(secondEntry)
- testScheduler.runCurrent()
-
- // WHEN: four more seconds have passed
- testScheduler.advanceTimeBy(4.seconds)
- testScheduler.runCurrent()
-
- // WHEN: the keyguard is no longer showing
- keyguardRepository.setKeyguardShowing(false)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE)
- )
- testScheduler.runCurrent()
-
- // WHEN: Keyguard is shown again
- keyguardRepository.setKeyguardShowing(true)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.LOCKSCREEN)
- )
- testScheduler.runCurrent()
-
- // THEN: The first notification is considered seen and is filtered out.
- assertThat(unseenFilter.shouldFilterOut(firstEntry, 0L)).isTrue()
-
- // THEN: The second notification is still considered unseen and is not filtered out
- assertThat(unseenFilter.shouldFilterOut(secondEntry, 0L)).isFalse()
- }
- }
-
- @Test
- fun unseenNotificationOnKeyguardNotMarkedAsSeenIfRemovedAfterThreshold() {
- // GIVEN: Keyguard is showing, not dozing
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setIsDozing(false)
- runKeyguardCoordinatorTest {
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.LOCKSCREEN)
- )
- testScheduler.runCurrent()
-
- // WHEN: a new notification is posted
- val entry = NotificationEntryBuilder().setId(1).build()
- collectionListener.onEntryAdded(entry)
- testScheduler.runCurrent()
-
- // WHEN: five more seconds have passed
- testScheduler.advanceTimeBy(5.seconds)
- testScheduler.runCurrent()
-
- // WHEN: the notification is removed
- collectionListener.onEntryRemoved(entry, 0)
- testScheduler.runCurrent()
-
- // WHEN: the notification is re-posted
- collectionListener.onEntryAdded(entry)
- testScheduler.runCurrent()
-
- // WHEN: one more second has passed
- testScheduler.advanceTimeBy(1.seconds)
- testScheduler.runCurrent()
-
- // WHEN: the keyguard is no longer showing
- keyguardRepository.setKeyguardShowing(false)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE)
- )
- testScheduler.runCurrent()
-
- // WHEN: Keyguard is shown again
- keyguardRepository.setKeyguardShowing(true)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.LOCKSCREEN)
- )
- testScheduler.runCurrent()
-
- // THEN: The notification is considered unseen and is not filtered out.
- assertThat(unseenFilter.shouldFilterOut(entry, 0L)).isFalse()
- }
- }
-
- @Test
- fun unseenNotificationOnKeyguardNotMarkedAsSeenIfRemovedBeforeThreshold() {
- // GIVEN: Keyguard is showing, not dozing
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setIsDozing(false)
- runKeyguardCoordinatorTest {
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.LOCKSCREEN)
- )
- testScheduler.runCurrent()
-
- // WHEN: a new notification is posted
- val entry = NotificationEntryBuilder().setId(1).build()
- collectionListener.onEntryAdded(entry)
- testScheduler.runCurrent()
-
- // WHEN: one second has passed
- testScheduler.advanceTimeBy(1.seconds)
- testScheduler.runCurrent()
-
- // WHEN: the notification is removed
- collectionListener.onEntryRemoved(entry, 0)
- testScheduler.runCurrent()
-
- // WHEN: the notification is re-posted
- collectionListener.onEntryAdded(entry)
- testScheduler.runCurrent()
-
- // WHEN: one more second has passed
- testScheduler.advanceTimeBy(1.seconds)
- testScheduler.runCurrent()
-
- // WHEN: the keyguard is no longer showing
- keyguardRepository.setKeyguardShowing(false)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE)
- )
- testScheduler.runCurrent()
-
- // WHEN: Keyguard is shown again
- keyguardRepository.setKeyguardShowing(true)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.LOCKSCREEN)
- )
- testScheduler.runCurrent()
-
- // THEN: The notification is considered unseen and is not filtered out.
- assertThat(unseenFilter.shouldFilterOut(entry, 0L)).isFalse()
- }
- }
-
- @Test
- fun unseenNotificationOnKeyguardNotMarkedAsSeenIfUpdatedBeforeThreshold() {
- // GIVEN: Keyguard is showing, not dozing
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setIsDozing(false)
- runKeyguardCoordinatorTest {
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.LOCKSCREEN)
- )
- testScheduler.runCurrent()
-
- // WHEN: a new notification is posted
- val entry = NotificationEntryBuilder().setId(1).build()
- collectionListener.onEntryAdded(entry)
- testScheduler.runCurrent()
-
- // WHEN: one second has passed
- testScheduler.advanceTimeBy(1.seconds)
- testScheduler.runCurrent()
-
- // WHEN: the notification is updated
- collectionListener.onEntryUpdated(entry)
- testScheduler.runCurrent()
-
- // WHEN: four more seconds have passed
- testScheduler.advanceTimeBy(4.seconds)
- testScheduler.runCurrent()
-
- // WHEN: the keyguard is no longer showing
- keyguardRepository.setKeyguardShowing(false)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE)
- )
- testScheduler.runCurrent()
-
- // WHEN: Keyguard is shown again
- keyguardRepository.setKeyguardShowing(true)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.LOCKSCREEN)
- )
- testScheduler.runCurrent()
-
- // THEN: The notification is considered unseen and is not filtered out.
- assertThat(unseenFilter.shouldFilterOut(entry, 0L)).isFalse()
- }
- }
-
private fun runKeyguardCoordinatorTest(
testBlock: suspend KeyguardCoordinatorTestScope.() -> Unit
) {
val testDispatcher = UnconfinedTestDispatcher()
val testScope = TestScope(testDispatcher)
- val fakeSettings =
- FakeSettings().apply {
- putInt(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, 1)
- }
+ val fakeSettings = FakeSettings().apply {
+ putInt(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, 1)
+ }
val seenNotificationsProvider = SeenNotificationsProviderImpl()
val keyguardCoordinator =
KeyguardCoordinator(
@@ -607,6 +387,7 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
keyguardRepository,
keyguardTransitionRepository,
mock<KeyguardCoordinatorLogger>(),
+ notifPipelineFlags,
testScope.backgroundScope,
sectionHeaderVisibilityProvider,
fakeSettings,
@@ -616,12 +397,11 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
keyguardCoordinator.attach(notifPipeline)
testScope.runTest(dispatchTimeoutMs = 1.seconds.inWholeMilliseconds) {
KeyguardCoordinatorTestScope(
- keyguardCoordinator,
- testScope,
- seenNotificationsProvider,
- fakeSettings,
- )
- .testBlock()
+ keyguardCoordinator,
+ testScope,
+ seenNotificationsProvider,
+ fakeSettings,
+ ).testBlock()
}
}
@@ -634,9 +414,10 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
val testScheduler: TestCoroutineScheduler
get() = scope.testScheduler
- val onStateChangeListener: Consumer<String> = withArgCaptor {
- verify(keyguardNotifVisibilityProvider).addOnStateChangedListener(capture())
- }
+ val onStateChangeListener: Consumer<String> =
+ withArgCaptor {
+ verify(keyguardNotifVisibilityProvider).addOnStateChangedListener(capture())
+ }
val unseenFilter: NotifFilter
get() = keyguardCoordinator.unseenNotifFilter
@@ -645,11 +426,11 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
verify(notifPipeline).addCollectionListener(capture())
}
- val onHeadsUpChangedListener: OnHeadsUpChangedListener
- get() = withArgCaptor { verify(headsUpManager).addListener(capture()) }
+ val onHeadsUpChangedListener: OnHeadsUpChangedListener get() =
+ withArgCaptor { verify(headsUpManager).addListener(capture()) }
- val statusBarStateListener: StatusBarStateController.StateListener
- get() = withArgCaptor { verify(statusBarStateController).addCallback(capture()) }
+ val statusBarStateListener: StatusBarStateController.StateListener get() =
+ withArgCaptor { verify(statusBarStateController).addCallback(capture()) }
var showOnlyUnseenNotifsOnKeyguardSetting: Boolean
get() =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 0e966dc655f7..18cf8765c472 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -44,10 +44,12 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.media.controls.ui.KeyguardMediaController;
import com.android.systemui.plugins.ActivityStarter;
@@ -122,6 +124,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private SysuiStatusBarStateController mSysuiStatusBarStateController;
@Mock private KeyguardBypassController mKeyguardBypassController;
@Mock private KeyguardInteractor mKeyguardInteractor;
+ @Mock private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
@Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@Mock private MetricsLogger mMetricsLogger;
@Mock private DumpManager mDumpManager;
@@ -145,7 +148,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private StackStateLogger mStackLogger;
@Mock private NotificationStackScrollLogger mLogger;
@Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
- @Mock private FeatureFlags mFeatureFlags;
+ private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@Mock private NotificationTargetsHelper mNotificationTargetsHelper;
@Mock private SecureSettings mSecureSettings;
@Mock private NotificationIconAreaController mIconAreaController;
@@ -163,6 +166,8 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, false);
+
when(mNotificationSwipeHelperBuilder.build()).thenReturn(mNotificationSwipeHelper);
}
@@ -262,28 +267,84 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
}
@Test
- public void testUpdateEmptyShadeView_bouncerShowing_hideEmptyView() {
+ public void testUpdateEmptyShadeView_bouncerShowing_flagOff_hideEmptyView() {
when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
initController(/* viewIsAttached= */ true);
- when(mCentralSurfaces.isBouncerShowing()).thenReturn(true);
+ // WHEN the flag is off and *only* CentralSurfaces has bouncer as showing
+ mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, false);
+ mController.setBouncerShowingFromCentralSurfaces(true);
+ when(mPrimaryBouncerInteractor.isBouncerShowing()).thenReturn(false);
+
setupShowEmptyShadeViewState(true);
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
+
+ // THEN the CentralSurfaces value is used. Since the bouncer is showing, we hide the empty
+ // view.
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ false,
/* areNotificationsHiddenInShade= */ false);
}
@Test
- public void testUpdateEmptyShadeView_bouncerNotShowing_showEmptyView() {
+ public void testUpdateEmptyShadeView_bouncerShowing_flagOn_hideEmptyView() {
when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
initController(/* viewIsAttached= */ true);
- when(mCentralSurfaces.isBouncerShowing()).thenReturn(false);
+ // WHEN the flag is on and *only* PrimaryBouncerInteractor has bouncer as showing
+ mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, true);
+ when(mPrimaryBouncerInteractor.isBouncerShowing()).thenReturn(true);
+ mController.setBouncerShowingFromCentralSurfaces(false);
+
setupShowEmptyShadeViewState(true);
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
+
+ // THEN the PrimaryBouncerInteractor value is used. Since the bouncer is showing, we
+ // hide the empty view.
+ verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+ /* visible= */ false,
+ /* areNotificationsHiddenInShade= */ false);
+ }
+
+ @Test
+ public void testUpdateEmptyShadeView_bouncerNotShowing_flagOff_showEmptyView() {
+ when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
+ initController(/* viewIsAttached= */ true);
+
+ // WHEN the flag is off and *only* CentralSurfaces has bouncer as not showing
+ mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, false);
+ mController.setBouncerShowingFromCentralSurfaces(false);
+ when(mPrimaryBouncerInteractor.isBouncerShowing()).thenReturn(true);
+
+ setupShowEmptyShadeViewState(true);
+ reset(mNotificationStackScrollLayout);
+ mController.updateShowEmptyShadeView();
+
+ // THEN the CentralSurfaces value is used. Since the bouncer isn't showing, we can show the
+ // empty view.
+ verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+ /* visible= */ true,
+ /* areNotificationsHiddenInShade= */ false);
+ }
+
+ @Test
+ public void testUpdateEmptyShadeView_bouncerNotShowing_flagOn_showEmptyView() {
+ when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
+ initController(/* viewIsAttached= */ true);
+
+ // WHEN the flag is on and *only* PrimaryBouncerInteractor has bouncer as not showing
+ mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, true);
+ when(mPrimaryBouncerInteractor.isBouncerShowing()).thenReturn(false);
+ mController.setBouncerShowingFromCentralSurfaces(true);
+
+ setupShowEmptyShadeViewState(true);
+ reset(mNotificationStackScrollLayout);
+ mController.updateShowEmptyShadeView();
+
+ // THEN the PrimaryBouncerInteractor value is used. Since the bouncer isn't showing, we
+ // can show the empty view.
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ true,
/* areNotificationsHiddenInShade= */ false);
@@ -548,6 +609,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mKeyguardMediaController,
mKeyguardBypassController,
mKeyguardInteractor,
+ mPrimaryBouncerInteractor,
mZenModeController,
mNotificationLockscreenUserManager,
Optional.<NotificationListViewModel>empty(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 5ed9a865de93..4762e57fa577 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -99,6 +99,7 @@ import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenu
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.biometrics.AuthRippleController;
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.charging.WiredChargingRippleController;
import com.android.systemui.classifier.FalsingCollectorFake;
@@ -113,7 +114,6 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.notetask.NoteTaskController;
@@ -122,6 +122,7 @@ import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.plugins.PluginDependencyProvider;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.brightness.BrightnessSliderController;
@@ -145,6 +146,7 @@ import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarState;
@@ -259,6 +261,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private AccessibilityFloatingMenuController mAccessibilityFloatingMenuController;
@Mock private SysuiColorExtractor mColorExtractor;
private WakefulnessLifecycle mWakefulnessLifecycle;
+ @Mock private PowerInteractor mPowerInteractor;
@Mock private ColorExtractor.GradientColors mGradientColors;
@Mock private PulseExpansionHandler mPulseExpansionHandler;
@Mock private NotificationWakeUpCoordinator mNotificationWakeUpCoordinator;
@@ -273,6 +276,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock private NotificationIconAreaController mNotificationIconAreaController;
@Mock private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
+ @Mock private NotificationShelfController mNotificationShelfController;
@Mock private DozeParameters mDozeParameters;
@Mock private Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy;
@Mock private LockscreenWallpaper mLockscreenWallpaper;
@@ -490,6 +494,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mColorExtractor,
mScreenLifecycle,
mWakefulnessLifecycle,
+ mPowerInteractor,
mStatusBarStateController,
Optional.of(mBubbles),
() -> mNoteTaskController,
@@ -499,6 +504,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
() -> mAssistManager,
configurationController,
mNotificationShadeWindowController,
+ mNotificationShelfController,
mDozeParameters,
mScrimController,
mLockscreenWallpaperLazy,
@@ -580,6 +586,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mCentralSurfaces.mPresenter = mNotificationPresenter;
mCentralSurfaces.mKeyguardIndicationController = mKeyguardIndicationController;
mCentralSurfaces.mBarService = mBarService;
+ mCentralSurfaces.mStackScrollerController = mStackScrollerController;
mCentralSurfaces.mStackScroller = mStackScroller;
mCentralSurfaces.mGestureWakeLock = mPowerManager.newWakeLock(
PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "sysui:GestureWakeLock");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 7e69efaaa640..5d2b59b6e00e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -67,6 +67,8 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
private float mQsExpansion;
private int mCutoutTopInset = 0;
private boolean mIsSplitShade = false;
+ private boolean mBypassEnabled = false;
+ private int mUnlockedStackScrollerPadding = 0;
private float mUdfpsTop = -1;
private float mClockBottom = SCREEN_HEIGHT / 2;
private boolean mClockTopAligned;
@@ -339,15 +341,52 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
}
@Test
- public void notifMinPaddingAlignedWithClockInSplitShadeMode() {
+ public void notifPadding_splitShade() {
givenLockScreen();
mIsSplitShade = true;
mKeyguardStatusHeight = 200;
// WHEN the position algorithm is run
positionClock();
// THEN the padding DOESN'T adjust for keyguard status height.
- assertThat(mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding())
- .isEqualTo(mKeyguardStatusBarHeaderHeight);
+ assertThat(mClockPositionAlgorithm.getLockscreenNotifPadding(/* nsslTop= */ 10))
+ .isEqualTo(mKeyguardStatusBarHeaderHeight - 10);
+ }
+
+ @Test
+ public void notifPadding_portraitShade_bypassOff() {
+ givenLockScreen();
+ mIsSplitShade = false;
+ mBypassEnabled = false;
+
+ // mMinTopMargin = 100 = 80 + max(20, 0)
+ mKeyguardStatusBarHeaderHeight = 80;
+ mUserSwitchHeight = 20;
+ when(mResources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin))
+ .thenReturn(0);
+
+ mKeyguardStatusHeight = 200;
+
+ // WHEN the position algorithm is run
+ positionClock();
+
+ // THEN padding = 300 = mMinTopMargin(100) + mKeyguardStatusHeight(200)
+ assertThat(mClockPositionAlgorithm.getLockscreenNotifPadding(/* nsslTop= */ 50))
+ .isEqualTo(300);
+ }
+
+ @Test
+ public void notifPadding_portraitShade_bypassOn() {
+ givenLockScreen();
+ mIsSplitShade = false;
+ mBypassEnabled = true;
+ mUnlockedStackScrollerPadding = 200;
+
+ // WHEN the position algorithm is run
+ positionClock();
+
+ // THEN padding = 150 = mUnlockedStackScrollerPadding(200) - nsslTop(50)
+ assertThat(mClockPositionAlgorithm.getLockscreenNotifPadding(/* nsslTop= */ 50))
+ .isEqualTo(150);
}
@Test
@@ -589,8 +628,8 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
0 /* userSwitchPreferredY */,
mDark,
ZERO_DRAG,
- false /* bypassEnabled */,
- 0 /* unlockedStackScrollerPadding */,
+ mBypassEnabled,
+ mUnlockedStackScrollerPadding,
mQsExpansion,
mCutoutTopInset,
mIsSplitShade,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index ef3a33248597..8f725bebfb16 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -16,19 +16,13 @@
package com.android.systemui.volume;
-import static android.media.AudioManager.RINGER_MODE_NORMAL;
-import static android.media.AudioManager.RINGER_MODE_SILENT;
-import static android.media.AudioManager.RINGER_MODE_VIBRATE;
-
import static com.android.systemui.volume.Events.DISMISS_REASON_UNKNOWN;
import static com.android.systemui.volume.Events.SHOW_REASON_UNKNOWN;
import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS;
import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotSame;
import static junit.framework.Assert.assertTrue;
-import static org.junit.Assume.assumeNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -299,7 +293,7 @@ public class VolumeDialogImplTest extends SysuiTestCase {
@Test
public void testSelectVibrateFromDrawer() {
final State initialUnsetState = new State();
- initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL;
+ initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL;
mDialog.onStateChangedH(initialUnsetState);
mActiveRinger.performClick();
@@ -313,7 +307,7 @@ public class VolumeDialogImplTest extends SysuiTestCase {
@Test
public void testSelectMuteFromDrawer() {
final State initialUnsetState = new State();
- initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL;
+ initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL;
mDialog.onStateChangedH(initialUnsetState);
mActiveRinger.performClick();
@@ -335,7 +329,7 @@ public class VolumeDialogImplTest extends SysuiTestCase {
// Make sure we've actually changed the ringer mode.
verify(mVolumeDialogController, times(1)).setRingerMode(
- RINGER_MODE_NORMAL, false);
+ AudioManager.RINGER_MODE_NORMAL, false);
}
/**
@@ -517,87 +511,6 @@ public class VolumeDialogImplTest extends SysuiTestCase {
}
}
- private enum RingerDrawerState {INIT, OPEN, CLOSE}
-
- @Test
- public void ringerModeNormal_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.INIT);
- }
-
- @Test
- public void ringerModeSilent_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.INIT);
- }
-
- @Test
- public void ringerModeVibrate_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.INIT);
- }
-
- @Test
- public void ringerModeNormal_openDrawer_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.OPEN);
- }
-
- @Test
- public void ringerModeSilent_openDrawer_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.OPEN);
- }
-
- @Test
- public void ringerModeVibrate_openDrawer_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.OPEN);
- }
-
- @Test
- public void ringerModeNormal_closeDrawer_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.CLOSE);
- }
-
- @Test
- public void ringerModeSilent_closeDrawer_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.CLOSE);
- }
-
- @Test
- public void ringerModeVibrate_closeDrawer_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.CLOSE);
- }
-
- /**
- * The content description should include ringer state, and the correct one.
- */
- private void assertRingerContainerDescribesItsState(int ringerMode,
- RingerDrawerState drawerState) {
- State state = createShellState();
- state.ringerModeInternal = ringerMode;
- mDialog.onStateChangedH(state);
-
- mDialog.show(SHOW_REASON_UNKNOWN);
-
- if (drawerState != RingerDrawerState.INIT) {
- // in both cases we first open the drawer
- mDialog.toggleRingerDrawer(true);
-
- if (drawerState == RingerDrawerState.CLOSE) {
- mDialog.toggleRingerDrawer(false);
- }
- }
-
- String ringerContainerDescription = mDialog.getSelectedRingerContainerDescription();
- assumeNotNull(ringerContainerDescription);
-
- String ringerDescription = mContext.getString(
- mDialog.getStringDescriptionResourceForRingerMode(ringerMode));
-
- if (drawerState == RingerDrawerState.OPEN) {
- assertEquals(ringerDescription, ringerContainerDescription);
- } else {
- assertNotSame(ringerDescription, ringerContainerDescription);
- assertTrue(ringerContainerDescription.startsWith(ringerDescription));
- }
- }
-
@After
public void teardown() {
if (mDialog != null) {
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 3efb6c304bec..c6e9a7d5e2e5 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -162,6 +162,8 @@ import java.util.concurrent.TimeUnit;
// TODO(b/180451994): ensure all incoming + outgoing calls have a cleared calling identity
public class VcnManagementService extends IVcnManagementService.Stub {
@NonNull private static final String TAG = VcnManagementService.class.getSimpleName();
+ @NonNull private static final String CONTEXT_ATTRIBUTION_TAG = "VCN";
+
private static final long DUMP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(5);
private static final int LOCAL_LOG_LINE_COUNT = 512;
@@ -223,7 +225,9 @@ public class VcnManagementService extends IVcnManagementService.Stub {
@VisibleForTesting(visibility = Visibility.PRIVATE)
VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) {
- mContext = requireNonNull(context, "Missing context");
+ mContext =
+ requireNonNull(context, "Missing context")
+ .createAttributionContext(CONTEXT_ATTRIBUTION_TAG);
mDeps = requireNonNull(deps, "Missing dependencies");
mLooper = mDeps.getLooper();
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index 9b4f9683d550..0d423d8a0a62 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -80,6 +80,7 @@ import java.util.List;
*/
public class VpnManagerService extends IVpnManager.Stub {
private static final String TAG = VpnManagerService.class.getSimpleName();
+ private static final String CONTEXT_ATTRIBUTION_TAG = "VPN_MANAGER";
@VisibleForTesting
protected final HandlerThread mHandlerThread;
@@ -157,7 +158,7 @@ public class VpnManagerService extends IVpnManager.Stub {
}
public VpnManagerService(Context context, Dependencies deps) {
- mContext = context;
+ mContext = context.createAttributionContext(CONTEXT_ATTRIBUTION_TAG);
mDeps = deps;
mHandlerThread = mDeps.makeHandlerThread();
mHandlerThread.start();
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index fd980727e12b..ba8a1b9d4690 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -149,7 +149,6 @@ import java.util.Locale;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
@@ -609,15 +608,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
return 1;
}
- AtomicReference<String> mimeType = new AtomicReference<>(intent.getType());
-
- if (mimeType.get() == null && intent.getData() != null
- && "content".equals(intent.getData().getScheme())) {
- mInterface.getMimeTypeFilterAsync(intent.getData(), mUserId,
- new RemoteCallback(result -> {
- mimeType.set(result.getPairValue());
- }));
- }
+ final String mimeType = intent.resolveType(mInternal.mContext);
do {
if (mStopOption) {
@@ -629,7 +620,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
int userIdForQuery = mInternal.mUserController.handleIncomingUser(
Binder.getCallingPid(), Binder.getCallingUid(), mUserId, false,
ALLOW_NON_FULL, "ActivityManagerShellCommand", null);
- List<ResolveInfo> activities = mPm.queryIntentActivities(intent, mimeType.get(),
+ List<ResolveInfo> activities = mPm.queryIntentActivities(intent, mimeType,
0, userIdForQuery).getList();
if (activities == null || activities.size() <= 0) {
getErrPrintWriter().println("Error: Intent does not match any activities: "
@@ -726,12 +717,12 @@ final class ActivityManagerShellCommand extends ShellCommand {
}
if (mWaitOption) {
result = mInternal.startActivityAndWait(null, SHELL_PACKAGE_NAME, null, intent,
- mimeType.get(), null, null, 0, mStartFlags, profilerInfo,
+ mimeType, null, null, 0, mStartFlags, profilerInfo,
options != null ? options.toBundle() : null, mUserId);
res = result.result;
} else {
res = mInternal.startActivityAsUserWithFeature(null, SHELL_PACKAGE_NAME, null,
- intent, mimeType.get(), null, null, 0, mStartFlags, profilerInfo,
+ intent, mimeType, null, null, 0, mStartFlags, profilerInfo,
options != null ? options.toBundle() : null, mUserId);
}
final long endTime = SystemClock.uptimeMillis();
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 76af50de0bd7..42a4a853f827 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -691,10 +691,10 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
// If this receiver is going to be skipped, skip it now itself and don't even enqueue
// it.
- final boolean wouldBeSkipped = (mSkipPolicy.shouldSkipMessage(r, receiver) != null);
- if (wouldBeSkipped) {
+ final String skipReason = mSkipPolicy.shouldSkipMessage(r, receiver);
+ if (skipReason != null) {
setDeliveryState(null, null, r, i, receiver, BroadcastRecord.DELIVERY_SKIPPED,
- "skipped by policy at enqueue");
+ "skipped by policy at enqueue: " + skipReason);
continue;
}
diff --git a/services/core/java/com/android/server/am/StackTracesDumpHelper.java b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
index 01fb0d1d8b18..af99684db6f4 100644
--- a/services/core/java/com/android/server/am/StackTracesDumpHelper.java
+++ b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
@@ -392,11 +392,9 @@ public class StackTracesDumpHelper {
if (TEMP_DUMP_TIME_LIMIT <= timeTaken) {
Slog.e(TAG, "Aborted stack trace dump (current primary pid=" + pid
+ "); deadline exceeded.");
- tmpTracesFile.delete();
if (latencyTracker != null) {
latencyTracker.dumpStackTracesTempFileTimedOut();
}
- return null;
}
if (DEBUG_ANR) {
Slog.d(TAG, "Done with primary pid " + pid + " in " + timeTaken + "ms"
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index fc72a771fa9c..29a19417b8fd 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1018,13 +1018,15 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
}
- /*package*/ void clearA2dpSuspended() {
+ /*package*/ void clearA2dpSuspended(boolean internalOnly) {
if (AudioService.DEBUG_COMM_RTE) {
- Log.v(TAG, "clearA2dpSuspended");
+ Log.v(TAG, "clearA2dpSuspended, internalOnly: " + internalOnly);
}
synchronized (mBluetoothAudioStateLock) {
mBluetoothA2dpSuspendedInt = false;
- mBluetoothA2dpSuspendedExt = false;
+ if (!internalOnly) {
+ mBluetoothA2dpSuspendedExt = false;
+ }
updateAudioHalBluetoothState();
}
}
@@ -1046,13 +1048,15 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
}
- /*package*/ void clearLeAudioSuspended() {
+ /*package*/ void clearLeAudioSuspended(boolean internalOnly) {
if (AudioService.DEBUG_COMM_RTE) {
- Log.v(TAG, "clearLeAudioSuspended");
+ Log.v(TAG, "clearLeAudioSuspended, internalOnly: " + internalOnly);
}
synchronized (mBluetoothAudioStateLock) {
mBluetoothLeSuspendedInt = false;
- mBluetoothLeSuspendedExt = false;
+ if (!internalOnly) {
+ mBluetoothLeSuspendedExt = false;
+ }
updateAudioHalBluetoothState();
}
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 219dda398ebf..ec85d577a8e7 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1547,7 +1547,7 @@ public class AudioDeviceInventory {
}
// Reset A2DP suspend state each time a new sink is connected
- mDeviceBroker.clearA2dpSuspended();
+ mDeviceBroker.clearA2dpSuspended(true /* internalOnly */);
// The convention for head tracking sensors associated with A2DP devices is to
// use a UUID derived from the MAC address as follows:
@@ -1970,7 +1970,7 @@ public class AudioDeviceInventory {
"LE Audio device addr=" + address + " now available").printLog(TAG));
}
// Reset LEA suspend state each time a new sink is connected
- mDeviceBroker.clearLeAudioSuspended();
+ mDeviceBroker.clearLeAudioSuspended(true /* internalOnly */);
UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada);
mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index ac03c82260f9..c177c4efb5ef 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -353,7 +353,6 @@ public class AudioService extends IAudioService.Stub
private static final int MSG_PLAY_SOUND_EFFECT = 5;
private static final int MSG_LOAD_SOUND_EFFECTS = 7;
private static final int MSG_SET_FORCE_USE = 8;
- private static final int MSG_BT_HEADSET_CNCT_FAILED = 9;
private static final int MSG_SET_ALL_VOLUMES = 10;
private static final int MSG_UNLOAD_SOUND_EFFECTS = 15;
private static final int MSG_SYSTEM_READY = 16;
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index e46c3cc4a285..c8a17e5eea5a 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -445,8 +445,8 @@ public class BtHelper {
/*package*/ synchronized void resetBluetoothSco() {
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- mDeviceBroker.clearA2dpSuspended();
- mDeviceBroker.clearLeAudioSuspended();
+ mDeviceBroker.clearA2dpSuspended(false /* internalOnly */);
+ mDeviceBroker.clearLeAudioSuspended(false /* internalOnly */);
mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco");
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index d6b6f771b861..5aa1175b92cd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -149,6 +149,17 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@NonNull String halInstanceName,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull BiometricContext biometricContext) {
+ this(context, biometricStateCallback, props, halInstanceName, lockoutResetDispatcher,
+ biometricContext, null /* daemon */);
+ }
+
+ @VisibleForTesting FaceProvider(@NonNull Context context,
+ @NonNull BiometricStateCallback biometricStateCallback,
+ @NonNull SensorProps[] props,
+ @NonNull String halInstanceName,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull BiometricContext biometricContext,
+ IFace daemon) {
mContext = context;
mBiometricStateCallback = biometricStateCallback;
mHalInstanceName = halInstanceName;
@@ -160,6 +171,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mTaskStackListener = new BiometricTaskStackListener();
mBiometricContext = biometricContext;
mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator();
+ mDaemon = daemon;
for (SensorProps prop : props) {
final int sensorId = prop.commonProps.sensorId;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 58ece898a9fe..9b2ea1589275 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -160,6 +160,17 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@NonNull BiometricContext biometricContext) {
+ this(context, biometricStateCallback, props, halInstanceName, lockoutResetDispatcher,
+ gestureAvailabilityDispatcher, biometricContext, null /* daemon */);
+ }
+
+ @VisibleForTesting FingerprintProvider(@NonNull Context context,
+ @NonNull BiometricStateCallback biometricStateCallback,
+ @NonNull SensorProps[] props, @NonNull String halInstanceName,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ @NonNull BiometricContext biometricContext,
+ IFingerprint daemon) {
mContext = context;
mBiometricStateCallback = biometricStateCallback;
mHalInstanceName = halInstanceName;
@@ -170,6 +181,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mTaskStackListener = new BiometricTaskStackListener();
mBiometricContext = biometricContext;
mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator();
+ mDaemon = daemon;
final List<SensorLocationInternal> workaroundLocations = getWorkaroundSensorProps(context);
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index d2dcc508d01f..41651fd5553c 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -126,6 +126,7 @@ public final class DreamManagerService extends SystemService {
private final boolean mDreamsActivatedOnChargeByDefault;
private final boolean mDreamsActivatedOnDockByDefault;
private final boolean mKeepDreamingWhenUnpluggingDefault;
+ private final boolean mDreamsDisabledByAmbientModeSuppressionConfig;
private final CopyOnWriteArrayList<DreamManagerInternal.DreamManagerStateListener>
mDreamManagerStateListeners = new CopyOnWriteArrayList<>();
@@ -239,6 +240,9 @@ public final class DreamManagerService extends SystemService {
mSettingsObserver = new SettingsObserver(mHandler);
mKeepDreamingWhenUnpluggingDefault = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_keepDreamingWhenUnplugging);
+ mDreamsDisabledByAmbientModeSuppressionConfig = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_dreamsDisabledByAmbientModeSuppressionConfig);
+
}
@Override
@@ -406,6 +410,13 @@ public final class DreamManagerService extends SystemService {
return false;
}
+ if (mDreamsDisabledByAmbientModeSuppressionConfig
+ && mPowerManagerInternal.isAmbientDisplaySuppressed()) {
+ // Don't dream if Bedtime (or something else) is suppressing ambient.
+ Slog.i(TAG, "Can't start dreaming because ambient is suppressed.");
+ return false;
+ }
+
if ((mWhenToDream & DREAM_ON_CHARGE) == DREAM_ON_CHARGE) {
return mIsCharging;
}
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index 611a61a2e272..3ac15947ab8e 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -16,6 +16,10 @@
package com.android.server.input;
+import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE;
+import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER;
+import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD;
+
import android.annotation.AnyThread;
import android.annotation.MainThread;
import android.annotation.NonNull;
@@ -67,6 +71,8 @@ import com.android.internal.inputmethod.InputMethodSubtypeHandle;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.XmlUtils;
+import com.android.server.input.KeyboardMetricsCollector.KeyboardConfigurationEvent;
+import com.android.server.input.KeyboardMetricsCollector.LayoutSelectionCriteria;
import com.android.server.inputmethod.InputMethodManagerInternal;
import libcore.io.Streams;
@@ -118,7 +124,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
// This cache stores "best-matched" layouts so that we don't need to run the matching
// algorithm repeatedly.
@GuardedBy("mKeyboardLayoutCache")
- private final Map<String, String> mKeyboardLayoutCache = new ArrayMap<>();
+ private final Map<String, KeyboardLayoutInfo> mKeyboardLayoutCache = new ArrayMap<>();
private final Object mImeInfoLock = new Object();
@Nullable
@GuardedBy("mImeInfoLock")
@@ -194,7 +200,8 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
setCurrentKeyboardLayoutForInputDevice(inputDevice.getIdentifier(), layout);
}
}
- config.setCurrentLayout(layout);
+ config.setCurrentLayout(
+ new KeyboardLayoutInfo(layout, LAYOUT_SELECTION_CRITERIA_USER));
if (layout == null) {
// In old settings show notification always until user manually selects a
// layout in the settings.
@@ -205,18 +212,19 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
final InputDeviceIdentifier identifier = inputDevice.getIdentifier();
final String key = getLayoutDescriptor(identifier);
Set<String> selectedLayouts = new HashSet<>();
- for (ImeInfo imeInfo : getImeInfoListForLayoutMapping()) {
+ List<ImeInfo> imeInfoList = getImeInfoListForLayoutMapping();
+ List<KeyboardLayoutInfo> layoutInfoList = new ArrayList<>();
+ boolean hasMissingLayout = false;
+ for (ImeInfo imeInfo : imeInfoList) {
// Check if the layout has been previously configured
- String layout = getKeyboardLayoutForInputDeviceInternal(identifier,
- new ImeInfo(imeInfo.mUserId, imeInfo.mImeSubtypeHandle,
- imeInfo.mImeSubtype));
- if (layout == null) {
- // If even one layout not configured properly, we need to ask user to configure
- // the keyboard properly from the Settings.
- selectedLayouts.clear();
- break;
+ KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(identifier,
+ imeInfo);
+ boolean noLayoutFound = layoutInfo == null || layoutInfo.mDescriptor == null;
+ if (!noLayoutFound) {
+ selectedLayouts.add(layoutInfo.mDescriptor);
}
- selectedLayouts.add(layout);
+ layoutInfoList.add(layoutInfo);
+ hasMissingLayout |= noLayoutFound;
}
if (DEBUG) {
@@ -225,24 +233,38 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
+ selectedLayouts);
}
+ // If even one layout not configured properly, we need to ask user to configure
+ // the keyboard properly from the Settings.
+ if (hasMissingLayout) {
+ selectedLayouts.clear();
+ }
+
config.setConfiguredLayouts(selectedLayouts);
// Update current layout: If there is a change then need to reload.
synchronized (mImeInfoLock) {
- String layout = getKeyboardLayoutForInputDeviceInternal(
+ KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(
inputDevice.getIdentifier(), mCurrentImeInfo);
- if (!Objects.equals(layout, config.getCurrentLayout())) {
- config.setCurrentLayout(layout);
+ if (!Objects.equals(layoutInfo, config.getCurrentLayout())) {
+ config.setCurrentLayout(layoutInfo);
mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
}
}
synchronized (mDataStore) {
try {
+ boolean isFirstConfiguration = !mDataStore.hasInputDeviceEntry(key);
if (mDataStore.setSelectedKeyboardLayouts(key, selectedLayouts)) {
// Need to show the notification only if layout selection changed
// from the previous configuration
needToShowNotification = true;
+
+ // Logging keyboard configuration data to statsd only if the
+ // configuration changed from the previous configuration. Currently
+ // only logging for New Settings UI where we are using IME to decide
+ // the layout information.
+ logKeyboardConfigurationEvent(inputDevice, imeInfoList, layoutInfoList,
+ isFirstConfiguration);
}
} finally {
mDataStore.saveIfNeeded();
@@ -252,8 +274,6 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
if (needToShowNotification) {
maybeUpdateNotification();
}
- // TODO (b/280421650): Implement logging statements using KeyboardMetricsCollector
- // for KeyboardConfigured atom
}
private String getDefaultKeyboardLayout(final InputDevice inputDevice) {
@@ -403,7 +423,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
@AnyThread
@Nullable
- public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) {
+ public KeyboardLayout getKeyboardLayout(@NonNull String keyboardLayoutDescriptor) {
Objects.requireNonNull(keyboardLayoutDescriptor,
"keyboardLayoutDescriptor must not be null");
@@ -751,8 +771,9 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
String keyboardLayoutDescriptor;
if (useNewSettingsUi()) {
synchronized (mImeInfoLock) {
- keyboardLayoutDescriptor = getKeyboardLayoutForInputDeviceInternal(identifier,
+ KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(identifier,
mCurrentImeInfo);
+ keyboardLayoutDescriptor = layoutInfo == null ? null : layoutInfo.mDescriptor;
}
} else {
keyboardLayoutDescriptor = getCurrentKeyboardLayoutForInputDevice(identifier);
@@ -789,13 +810,13 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
return null;
}
InputMethodSubtypeHandle subtypeHandle = InputMethodSubtypeHandle.of(imeInfo, imeSubtype);
- String layout = getKeyboardLayoutForInputDeviceInternal(identifier,
+ KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(identifier,
new ImeInfo(userId, subtypeHandle, imeSubtype));
if (DEBUG) {
Slog.d(TAG, "getKeyboardLayoutForInputDevice() " + identifier.toString() + ", userId : "
- + userId + ", subtypeHandle = " + subtypeHandle + " -> " + layout);
+ + userId + ", subtypeHandle = " + subtypeHandle + " -> " + layoutInfo);
}
- return layout;
+ return layoutInfo != null ? layoutInfo.mDescriptor : null;
}
@AnyThread
@@ -926,11 +947,11 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
for (int i = 0; i < mConfiguredKeyboards.size(); i++) {
InputDevice inputDevice = Objects.requireNonNull(
getInputDevice(mConfiguredKeyboards.keyAt(i)));
- String layout = getKeyboardLayoutForInputDeviceInternal(inputDevice.getIdentifier(),
- mCurrentImeInfo);
+ KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(
+ inputDevice.getIdentifier(), mCurrentImeInfo);
KeyboardConfiguration config = mConfiguredKeyboards.valueAt(i);
- if (!Objects.equals(layout, config.getCurrentLayout())) {
- config.setCurrentLayout(layout);
+ if (!Objects.equals(layoutInfo, config.getCurrentLayout())) {
+ config.setCurrentLayout(layoutInfo);
mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
return;
}
@@ -939,39 +960,40 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
}
@Nullable
- private String getKeyboardLayoutForInputDeviceInternal(InputDeviceIdentifier identifier,
- @Nullable ImeInfo imeInfo) {
+ private KeyboardLayoutInfo getKeyboardLayoutForInputDeviceInternal(
+ InputDeviceIdentifier identifier, @Nullable ImeInfo imeInfo) {
InputDevice inputDevice = getInputDevice(identifier);
if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
return null;
}
String key = createLayoutKey(identifier, imeInfo);
- String layout;
synchronized (mDataStore) {
- layout = mDataStore.getKeyboardLayout(getLayoutDescriptor(identifier), key);
- }
- if (layout == null) {
- synchronized (mKeyboardLayoutCache) {
- // Check Auto-selected layout cache to see if layout had been previously selected
- if (mKeyboardLayoutCache.containsKey(key)) {
- layout = mKeyboardLayoutCache.get(key);
- } else {
- // NOTE: This list is already filtered based on IME Script code
- KeyboardLayout[] layoutList = getKeyboardLayoutListForInputDeviceInternal(
- identifier, imeInfo);
- // Call auto-matching algorithm to find the best matching layout
- layout = getDefaultKeyboardLayoutBasedOnImeInfo(inputDevice, imeInfo,
- layoutList);
- mKeyboardLayoutCache.put(key, layout);
- }
+ String layout = mDataStore.getKeyboardLayout(getLayoutDescriptor(identifier), key);
+ if (layout != null) {
+ return new KeyboardLayoutInfo(layout, LAYOUT_SELECTION_CRITERIA_USER);
+ }
+ }
+
+ synchronized (mKeyboardLayoutCache) {
+ // Check Auto-selected layout cache to see if layout had been previously selected
+ if (mKeyboardLayoutCache.containsKey(key)) {
+ return mKeyboardLayoutCache.get(key);
+ } else {
+ // NOTE: This list is already filtered based on IME Script code
+ KeyboardLayout[] layoutList = getKeyboardLayoutListForInputDeviceInternal(
+ identifier, imeInfo);
+ // Call auto-matching algorithm to find the best matching layout
+ KeyboardLayoutInfo layoutInfo =
+ getDefaultKeyboardLayoutBasedOnImeInfo(inputDevice, imeInfo, layoutList);
+ mKeyboardLayoutCache.put(key, layoutInfo);
+ return layoutInfo;
}
}
- return layout;
}
@Nullable
- private static String getDefaultKeyboardLayoutBasedOnImeInfo(InputDevice inputDevice,
- @Nullable ImeInfo imeInfo, KeyboardLayout[] layoutList) {
+ private static KeyboardLayoutInfo getDefaultKeyboardLayoutBasedOnImeInfo(
+ InputDevice inputDevice, @Nullable ImeInfo imeInfo, KeyboardLayout[] layoutList) {
Arrays.sort(layoutList);
// Check <VendorID, ProductID> matching for explicitly declared custom KCM files.
@@ -984,7 +1006,8 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
+ "vendor and product Ids. " + inputDevice.getIdentifier()
+ " : " + layout.getDescriptor());
}
- return layout.getDescriptor();
+ return new KeyboardLayoutInfo(layout.getDescriptor(),
+ LAYOUT_SELECTION_CRITERIA_DEVICE);
}
}
@@ -1001,7 +1024,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
+ "HW information (Language tag and Layout type). "
+ inputDevice.getIdentifier() + " : " + layoutDesc);
}
- return layoutDesc;
+ return new KeyboardLayoutInfo(layoutDesc, LAYOUT_SELECTION_CRITERIA_DEVICE);
}
}
@@ -1023,7 +1046,10 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
+ "IME locale matching. " + inputDevice.getIdentifier() + " : "
+ layoutDesc);
}
- return layoutDesc;
+ if (layoutDesc != null) {
+ return new KeyboardLayoutInfo(layoutDesc, LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD);
+ }
+ return null;
}
@Nullable
@@ -1229,6 +1255,26 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
}
}
+ private void logKeyboardConfigurationEvent(@NonNull InputDevice inputDevice,
+ @NonNull List<ImeInfo> imeInfoList, @NonNull List<KeyboardLayoutInfo> layoutInfoList,
+ boolean isFirstConfiguration) {
+ if (imeInfoList.isEmpty() || layoutInfoList.isEmpty()) {
+ return;
+ }
+ KeyboardConfigurationEvent.Builder configurationEventBuilder =
+ new KeyboardConfigurationEvent.Builder(inputDevice).setIsFirstTimeConfiguration(
+ isFirstConfiguration);
+ for (int i = 0; i < imeInfoList.size(); i++) {
+ KeyboardLayoutInfo layoutInfo = layoutInfoList.get(i);
+ boolean noLayoutFound = layoutInfo == null || layoutInfo.mDescriptor == null;
+ configurationEventBuilder.addLayoutSelection(imeInfoList.get(i).mImeSubtype,
+ noLayoutFound ? null : getKeyboardLayout(layoutInfo.mDescriptor),
+ noLayoutFound ? LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
+ : layoutInfo.mSelectionCriteria);
+ }
+ KeyboardMetricsCollector.logKeyboardConfiguredAtom(configurationEventBuilder.build());
+ }
+
private boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_EXISTING_DEVICES:
@@ -1411,7 +1457,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
// If null, it means no layout is selected for the device.
@Nullable
- private String mCurrentLayout;
+ private KeyboardLayoutInfo mCurrentLayout;
private boolean hasConfiguredLayouts() {
return mConfiguredLayouts != null && !mConfiguredLayouts.isEmpty();
@@ -1427,15 +1473,42 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
}
@Nullable
- private String getCurrentLayout() {
+ private KeyboardLayoutInfo getCurrentLayout() {
return mCurrentLayout;
}
- private void setCurrentLayout(String currentLayout) {
+ private void setCurrentLayout(KeyboardLayoutInfo currentLayout) {
mCurrentLayout = currentLayout;
}
}
+ private static class KeyboardLayoutInfo {
+ @Nullable
+ private final String mDescriptor;
+ @LayoutSelectionCriteria
+ private final int mSelectionCriteria;
+
+ private KeyboardLayoutInfo(@Nullable String descriptor,
+ @LayoutSelectionCriteria int selectionCriteria) {
+ mDescriptor = descriptor;
+ mSelectionCriteria = selectionCriteria;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof KeyboardLayoutInfo) {
+ return Objects.equals(mDescriptor, ((KeyboardLayoutInfo) obj).mDescriptor)
+ && mSelectionCriteria == ((KeyboardLayoutInfo) obj).mSelectionCriteria;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * mSelectionCriteria + mDescriptor.hashCode();
+ }
+ }
+
private interface KeyboardLayoutVisitor {
void visitKeyboardLayout(Resources resources,
int keyboardLayoutResId, KeyboardLayout layout);
diff --git a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
index b8f57f555c0e..19fa7a8e0aca 100644
--- a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
+++ b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
@@ -19,16 +19,25 @@ package com.android.server.input;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.hardware.input.KeyboardLayout;
+import android.icu.util.ULocale;
+import android.util.Log;
+import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.InputDevice;
+import android.view.inputmethod.InputMethodSubtype;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.KeyboardConfiguredProto.KeyboardLayoutConfig;
import com.android.internal.os.KeyboardConfiguredProto.RepeatedKeyboardLayoutConfig;
import com.android.internal.util.FrameworkStatsLog;
import java.lang.annotation.Retention;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* Collect Keyboard metrics
@@ -36,6 +45,30 @@ import java.util.List;
public final class KeyboardMetricsCollector {
private static final String TAG = "KeyboardMetricCollector";
+ // To enable these logs, run: 'adb shell setprop log.tag.KeyboardMetricCollector DEBUG'
+ // (requires restart)
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ @Retention(SOURCE)
+ @IntDef(prefix = { "LAYOUT_SELECTION_CRITERIA_" }, value = {
+ LAYOUT_SELECTION_CRITERIA_USER,
+ LAYOUT_SELECTION_CRITERIA_DEVICE,
+ LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
+ })
+ public @interface LayoutSelectionCriteria {}
+
+ /** Manual selection by user */
+ public static final int LAYOUT_SELECTION_CRITERIA_USER = 0;
+
+ /** Auto-detection based on device provided language tag and layout type */
+ public static final int LAYOUT_SELECTION_CRITERIA_DEVICE = 1;
+
+ /** Auto-detection based on IME provided language tag and layout type */
+ public static final int LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD = 2;
+
+ @VisibleForTesting
+ static final String DEFAULT_LAYOUT = "Default";
+
/**
* Log keyboard system shortcuts for the proto
* {@link com.android.os.input.KeyboardSystemsEventReported}
@@ -43,120 +76,233 @@ public final class KeyboardMetricsCollector {
*/
public static void logKeyboardSystemsEventReportedAtom(InputDevice inputDevice,
int keyboardSystemEvent, int[] keyCode, int modifierState) {
- int vendor_id = inputDevice.getVendorId();
- int product_id = inputDevice.getProductId();
+ int vendorId = inputDevice.getVendorId();
+ int productId = inputDevice.getProductId();
FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED,
- vendor_id, product_id, keyboardSystemEvent, keyCode, modifierState);
+ vendorId, productId, keyboardSystemEvent, keyCode, modifierState);
}
/**
* Function to log the KeyboardConfigured
* {@link com.android.os.input.KeyboardConfigured} atom
*
- * @param inputDevice Input device
- * @param keyboardLayoutConfigurations List of keyboard configurations
- * @param isFirstTimeConfiguration Whether keyboard is configured for the first time
+ * @param event {@link KeyboardConfigurationEvent} contains information about keyboard
+ * configuration. Use {@link KeyboardConfigurationEvent.Builder} to create the
+ * configuration event to log.
*/
- public static void logKeyboardConfiguredAtom(InputDevice inputDevice,
- List<KeyboardLayoutConfiguration> keyboardLayoutConfigurations,
- boolean isFirstTimeConfiguration) {
- int vendor_id = inputDevice.getVendorId();
- int product_id = inputDevice.getProductId();
-
+ public static void logKeyboardConfiguredAtom(KeyboardConfigurationEvent event) {
// Creating proto to log nested field KeyboardLayoutConfig in atom
ProtoOutputStream proto = new ProtoOutputStream();
- for (KeyboardLayoutConfiguration keyboardLayoutConfiguration :
- keyboardLayoutConfigurations) {
- addKeyboardLayoutConfigurationToProto(proto, keyboardLayoutConfiguration);
+ for (LayoutConfiguration layoutConfiguration : event.getLayoutConfigurations()) {
+ addKeyboardLayoutConfigurationToProto(proto, layoutConfiguration);
}
// Push the atom to Statsd
FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_CONFIGURED,
- isFirstTimeConfiguration, vendor_id, product_id, proto.getBytes());
+ event.isFirstConfiguration(), event.getVendorId(), event.getProductId(),
+ proto.getBytes());
+
+ if (DEBUG) {
+ Slog.d(TAG, "Logging Keyboard configuration event: " + event);
+ }
}
/**
* Populate the KeyboardLayoutConfig proto which is a repeated proto
* in the RepeatedKeyboardLayoutConfig proto with values from the
- * {@link KeyboardLayoutConfiguration} class
+ * {@link LayoutConfiguration} class
* The proto definitions can be found at:
* "frameworks/proto_logging/stats/atoms/input/input_extension_atoms.proto"
*
* @param proto Representing the nested proto RepeatedKeyboardLayoutConfig
- * @param keyboardLayoutConfiguration Class containing the fields for populating the
+ * @param layoutConfiguration Class containing the fields for populating the
* KeyboardLayoutConfig proto
*/
private static void addKeyboardLayoutConfigurationToProto(ProtoOutputStream proto,
- KeyboardLayoutConfiguration keyboardLayoutConfiguration) {
+ LayoutConfiguration layoutConfiguration) {
// Start a new KeyboardLayoutConfig proto.
long keyboardLayoutConfigToken = proto.start(
RepeatedKeyboardLayoutConfig.KEYBOARD_LAYOUT_CONFIG);
proto.write(KeyboardLayoutConfig.KEYBOARD_LANGUAGE_TAG,
- keyboardLayoutConfiguration.getKeyboardLanguageTag());
+ layoutConfiguration.keyboardLanguageTag);
proto.write(KeyboardLayoutConfig.KEYBOARD_LAYOUT_TYPE,
- keyboardLayoutConfiguration.getKeyboardLayoutType());
+ layoutConfiguration.keyboardLayoutType);
proto.write(KeyboardLayoutConfig.KEYBOARD_LAYOUT_NAME,
- keyboardLayoutConfiguration.getKeyboardLayoutName());
+ layoutConfiguration.keyboardLayoutName);
proto.write(KeyboardLayoutConfig.LAYOUT_SELECTION_CRITERIA,
- keyboardLayoutConfiguration.getLayoutSelectionCriteria());
+ layoutConfiguration.layoutSelectionCriteria);
proto.end(keyboardLayoutConfigToken);
}
/**
- * Java class representing the proto KeyboardLayoutConfig defined in
- * "frameworks/proto_logging/stats/atoms/input/input_extension_atoms.proto"
+ * Class representing the proto KeyboardLayoutConfig defined in
+ * "frameworks/proto_logging/stats/atoms/input/input_extension_atoms.proto
*
* @see com.android.os.input.KeyboardConfigured
*/
- public static class KeyboardLayoutConfiguration {
- // KeyboardLayoutType in "frameworks/base/core/res/res/values/attrs.xml"
- // contains mapping for enums to int
- int mKeyboardLayoutType;
- String mKeyboardLanguageTag;
- KeyboardLayout mKeyboardLayout;
- @LayoutSelectionCriteria int mLayoutSelectionCriteria;
-
- @Retention(SOURCE)
- @IntDef(prefix = { "LAYOUT_SELECTION_CRITERIA_" }, value = {
- LAYOUT_SELECTION_CRITERIA_USER,
- LAYOUT_SELECTION_CRITERIA_DEVICE,
- LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
- })
- public @interface LayoutSelectionCriteria {}
-
- /** Manual selection by user */
- public static final int LAYOUT_SELECTION_CRITERIA_USER = 0;
-
- /** Auto-detection based on device provided language tag and layout type */
- public static final int LAYOUT_SELECTION_CRITERIA_DEVICE = 1;
-
- /** Auto-detection based on IME provided language tag and layout type */
- public static final int LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD = 2;
-
- KeyboardLayoutConfiguration(int keyboardLayoutType,
- String keyboardLanguageTag,
- KeyboardLayout keyboardLayout,
- @LayoutSelectionCriteria int layoutSelectionCriteria) {
- mKeyboardLayoutType = keyboardLayoutType;
- mKeyboardLanguageTag = keyboardLanguageTag;
- mKeyboardLayout = keyboardLayout;
- mLayoutSelectionCriteria = layoutSelectionCriteria;
+ public static class KeyboardConfigurationEvent {
+
+ private final InputDevice mInputDevice;
+ private final boolean mIsFirstConfiguration;
+ private final List<LayoutConfiguration> mLayoutConfigurations;
+
+ private KeyboardConfigurationEvent(InputDevice inputDevice, boolean isFirstConfiguration,
+ List<LayoutConfiguration> layoutConfigurations) {
+ mInputDevice = inputDevice;
+ mIsFirstConfiguration = isFirstConfiguration;
+ mLayoutConfigurations = layoutConfigurations;
+ }
+
+ public int getVendorId() {
+ return mInputDevice.getVendorId();
}
- int getKeyboardLayoutType() {
- return mKeyboardLayoutType;
+
+ public int getProductId() {
+ return mInputDevice.getProductId();
}
- String getKeyboardLanguageTag() {
- return mKeyboardLanguageTag;
+ public boolean isFirstConfiguration() {
+ return mIsFirstConfiguration;
}
- String getKeyboardLayoutName() {
- return mKeyboardLayout.getLabel();
+ public List<LayoutConfiguration> getLayoutConfigurations() {
+ return mLayoutConfigurations;
}
- @LayoutSelectionCriteria int getLayoutSelectionCriteria() {
- return mLayoutSelectionCriteria;
+ @Override
+ public String toString() {
+ return "InputDevice = {VendorId = " + Integer.toHexString(getVendorId())
+ + ", ProductId = " + Integer.toHexString(getProductId())
+ + "}, isFirstConfiguration = " + mIsFirstConfiguration
+ + ", LayoutConfigurations = " + mLayoutConfigurations;
}
+
+ /**
+ * Builder class to help create {@link KeyboardConfigurationEvent}.
+ */
+ public static class Builder {
+ @NonNull
+ private final InputDevice mInputDevice;
+ private boolean mIsFirstConfiguration;
+ private final List<InputMethodSubtype> mImeSubtypeList = new ArrayList<>();
+ private final List<KeyboardLayout> mSelectedLayoutList = new ArrayList<>();
+ private final List<Integer> mLayoutSelectionCriteriaList = new ArrayList<>();
+
+ public Builder(@NonNull InputDevice inputDevice) {
+ Objects.requireNonNull(inputDevice, "InputDevice provided should not be null");
+ mInputDevice = inputDevice;
+ }
+
+ /**
+ * Set whether this is the first time this keyboard is configured.
+ */
+ public Builder setIsFirstTimeConfiguration(boolean isFirstTimeConfiguration) {
+ mIsFirstConfiguration = isFirstTimeConfiguration;
+ return this;
+ }
+
+ /**
+ * Adds keyboard layout configuration info for a particular IME subtype language
+ */
+ public Builder addLayoutSelection(@NonNull InputMethodSubtype imeSubtype,
+ @Nullable KeyboardLayout selectedLayout,
+ @LayoutSelectionCriteria int layoutSelectionCriteria) {
+ Objects.requireNonNull(imeSubtype, "IME subtype provided should not be null");
+ if (!isValidSelectionCriteria(layoutSelectionCriteria)) {
+ throw new IllegalStateException("Invalid layout selection criteria");
+ }
+ mImeSubtypeList.add(imeSubtype);
+ mSelectedLayoutList.add(selectedLayout);
+ mLayoutSelectionCriteriaList.add(layoutSelectionCriteria);
+ return this;
+ }
+
+ /**
+ * Creates {@link KeyboardConfigurationEvent} from the provided information
+ */
+ public KeyboardConfigurationEvent build() {
+ int size = mImeSubtypeList.size();
+ if (size == 0) {
+ throw new IllegalStateException("Should have at least one configuration");
+ }
+ List<LayoutConfiguration> configurationList = new ArrayList<>();
+ for (int i = 0; i < size; i++) {
+ KeyboardLayout selectedLayout = mSelectedLayoutList.get(i);
+ @LayoutSelectionCriteria int layoutSelectionCriteria =
+ mLayoutSelectionCriteriaList.get(i);
+ InputMethodSubtype imeSubtype = mImeSubtypeList.get(i);
+ String keyboardLanguageTag;
+ String keyboardLayoutStringType;
+ if (layoutSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEVICE) {
+ keyboardLanguageTag = mInputDevice.getKeyboardLanguageTag();
+ keyboardLayoutStringType = mInputDevice.getKeyboardLayoutType();
+ } else {
+ ULocale pkLocale = imeSubtype.getPhysicalKeyboardHintLanguageTag();
+ keyboardLanguageTag = pkLocale != null ? pkLocale.toLanguageTag()
+ : imeSubtype.getCanonicalizedLanguageTag();
+ keyboardLayoutStringType = imeSubtype.getPhysicalKeyboardHintLayoutType();
+ }
+ // Sanitize null values
+ String keyboardLayoutName =
+ selectedLayout == null ? DEFAULT_LAYOUT : selectedLayout.getLabel();
+ keyboardLanguageTag = keyboardLanguageTag == null ? "" : keyboardLanguageTag;
+ int keyboardLayoutType = KeyboardLayout.LayoutType.getLayoutTypeEnumValue(
+ keyboardLayoutStringType);
+
+ configurationList.add(
+ new LayoutConfiguration(keyboardLayoutType, keyboardLanguageTag,
+ keyboardLayoutName, layoutSelectionCriteria));
+ }
+ return new KeyboardConfigurationEvent(mInputDevice, mIsFirstConfiguration,
+ configurationList);
+ }
+ }
+ }
+
+ @VisibleForTesting
+ static class LayoutConfiguration {
+ // This should match enum values defined in "frameworks/base/core/res/res/values/attrs.xml"
+ public final int keyboardLayoutType;
+ public final String keyboardLanguageTag;
+ public final String keyboardLayoutName;
+ @LayoutSelectionCriteria
+ public final int layoutSelectionCriteria;
+
+ private LayoutConfiguration(int keyboardLayoutType, String keyboardLanguageTag,
+ String keyboardLayoutName, @LayoutSelectionCriteria int layoutSelectionCriteria) {
+ this.keyboardLayoutType = keyboardLayoutType;
+ this.keyboardLanguageTag = keyboardLanguageTag;
+ this.keyboardLayoutName = keyboardLayoutName;
+ this.layoutSelectionCriteria = layoutSelectionCriteria;
+ }
+
+ @Override
+ public String toString() {
+ return "{keyboardLanguageTag = " + keyboardLanguageTag + " keyboardLayoutType = "
+ + KeyboardLayout.LayoutType.getLayoutNameFromValue(keyboardLayoutType)
+ + " keyboardLayoutName = " + keyboardLayoutName + " layoutSelectionCriteria = "
+ + getStringForSelectionCriteria(layoutSelectionCriteria) + "}";
+ }
+ }
+
+ private static String getStringForSelectionCriteria(
+ @LayoutSelectionCriteria int layoutSelectionCriteria) {
+ switch (layoutSelectionCriteria) {
+ case LAYOUT_SELECTION_CRITERIA_USER:
+ return "LAYOUT_SELECTION_CRITERIA_USER";
+ case LAYOUT_SELECTION_CRITERIA_DEVICE:
+ return "LAYOUT_SELECTION_CRITERIA_DEVICE";
+ case LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD:
+ return "LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD";
+ default:
+ return "INVALID_CRITERIA";
+ }
+ }
+
+ private static boolean isValidSelectionCriteria(int layoutSelectionCriteria) {
+ return layoutSelectionCriteria == LAYOUT_SELECTION_CRITERIA_USER
+ || layoutSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEVICE
+ || layoutSelectionCriteria == LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD;
}
}
diff --git a/services/core/java/com/android/server/input/PersistentDataStore.java b/services/core/java/com/android/server/input/PersistentDataStore.java
index bce210d0a4a4..31083fd5de03 100644
--- a/services/core/java/com/android/server/input/PersistentDataStore.java
+++ b/services/core/java/com/android/server/input/PersistentDataStore.java
@@ -101,6 +101,10 @@ final class PersistentDataStore {
}
}
+ public boolean hasInputDeviceEntry(String inputDeviceDescriptor) {
+ return getInputDeviceState(inputDeviceDescriptor) != null;
+ }
+
public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) {
InputDeviceState state = getInputDeviceState(inputDeviceDescriptor);
if (state == null) {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 2e62ef4f8566..38f153b222a3 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -41,6 +41,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.internal.widget.LockPatternUtils.USER_FRP;
import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE;
+import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW;
import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential;
import static com.android.server.locksettings.SyntheticPasswordManager.TOKEN_TYPE_STRONG;
@@ -278,8 +279,6 @@ public class LockSettingsService extends ILockSettings.Stub {
protected IGateKeeperService mGateKeeperService;
protected IAuthSecret mAuthSecretService;
- private static final String GSI_RUNNING_PROP = "ro.gsid.image_running";
-
/**
* The UIDs that are used for system credential storage in keystore.
*/
@@ -311,6 +310,7 @@ public class LockSettingsService extends ILockSettings.Stub {
if (phase == PHASE_ACTIVITY_MANAGER_READY) {
mLockSettingsService.migrateOldDataAfterSystemReady();
mLockSettingsService.loadEscrowData();
+ mLockSettingsService.deleteRepairModePersistentDataIfNeeded();
}
}
@@ -541,7 +541,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
public boolean isGsiRunning() {
- return SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0;
+ return LockPatternUtils.isGsiRunning();
}
public FingerprintManager getFingerprintManager() {
@@ -949,6 +949,16 @@ public class LockSettingsService extends ILockSettings.Stub {
return success;
}
+ @VisibleForTesting
+ void deleteRepairModePersistentDataIfNeeded() {
+ if (!LockPatternUtils.isRepairModeSupported(mContext)
+ || LockPatternUtils.isRepairModeActive(mContext)
+ || mInjector.isGsiRunning()) {
+ return;
+ }
+ mStorage.deleteRepairModePersistentData();
+ }
+
// This is called when Weaver is guaranteed to be available (if the device supports Weaver).
// It does any synthetic password related work that was delayed from earlier in the boot.
private void onThirdPartyAppsStarted() {
@@ -2202,6 +2212,12 @@ public class LockSettingsService extends ILockSettings.Stub {
response = authResult.gkResponse;
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
+ if ((flags & VERIFY_FLAG_WRITE_REPAIR_MODE_PW) != 0) {
+ if (!mSpManager.writeRepairModeCredentialLocked(protectorId, userId)) {
+ Slog.e(TAG, "Failed to write repair mode credential");
+ return VerifyCredentialResponse.ERROR;
+ }
+ }
// credential has matched
mBiometricDeferredQueue.addPendingLockoutResetForUser(userId,
authResult.syntheticPassword.deriveGkPassword());
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 2fa637e030f1..1ace6e5452ad 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -90,6 +90,9 @@ class LockSettingsStorage {
private static final String SYNTHETIC_PASSWORD_DIRECTORY = "spblob/";
+ private static final String REPAIR_MODE_DIRECTORY = "repair-mode/";
+ private static final String REPAIR_MODE_PERSISTENT_FILE = "pst";
+
private static final Object DEFAULT = new Object();
private static final String[] SETTINGS_TO_BACKUP = new String[] {
@@ -390,6 +393,29 @@ class LockSettingsStorage {
}
}
+ @VisibleForTesting
+ File getRepairModePersistentDataFile() {
+ final File directory = new File(Environment.getMetadataDirectory(), REPAIR_MODE_DIRECTORY);
+ return new File(directory, REPAIR_MODE_PERSISTENT_FILE);
+ }
+
+ public PersistentData readRepairModePersistentData() {
+ final byte[] data = readFile(getRepairModePersistentDataFile());
+ if (data == null) {
+ return PersistentData.NONE;
+ }
+ return PersistentData.fromBytes(data);
+ }
+
+ public void writeRepairModePersistentData(int persistentType, int userId, byte[] payload) {
+ writeFile(getRepairModePersistentDataFile(),
+ PersistentData.toBytes(persistentType, userId, /* qualityForUi= */0, payload));
+ }
+
+ public void deleteRepairModePersistentData() {
+ deleteFile(getRepairModePersistentDataFile());
+ }
+
/**
* Writes the synthetic password state file for the given user ID, protector ID, and state name.
* If the file already exists, then it is atomically replaced.
@@ -583,6 +609,17 @@ class LockSettingsStorage {
}
}
+ /**
+ * Provides a concrete data structure to represent the minimal information from
+ * a user's LSKF-based SP protector that is needed to verify the user's LSKF,
+ * in combination with the corresponding Gatekeeper enrollment or Weaver slot.
+ * It can be stored in {@link com.android.server.PersistentDataBlockService} for
+ * FRP to live across factory resets not initiated via the Settings UI.
+ * Written to {@link #REPAIR_MODE_PERSISTENT_FILE} to support verification for
+ * exiting repair mode, since the device runs with an empty data partition in
+ * repair mode and the same credential be provided to exit repair mode is
+ * required.
+ */
public static class PersistentData {
static final byte VERSION_1 = 1;
static final int VERSION_1_HEADER_SIZE = 1 + 1 + 4 + 4;
@@ -685,6 +722,19 @@ class LockSettingsStorage {
}
pw.decreaseIndent();
}
+ // Dump repair mode file states
+ final File repairModeFile = getRepairModePersistentDataFile();
+ if (repairModeFile.exists()) {
+ pw.println(TextUtils.formatSimple("Repair Mode [%s]:", repairModeFile.getParent()));
+ pw.increaseIndent();
+ pw.println(TextUtils.formatSimple("%6d %s %s", repairModeFile.length(),
+ LockSettingsService.timestampToString(repairModeFile.lastModified()),
+ repairModeFile.getName()));
+ final PersistentData data = readRepairModePersistentData();
+ pw.println(TextUtils.formatSimple("type: %d, user id: %d, payload size: %d",
+ data.type, data.userId, data.payload != null ? data.payload.length : 0));
+ pw.decreaseIndent();
+ }
}
static class DatabaseHelper extends SQLiteOpenHelper {
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 66f862ac9205..d74d304be730 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -1114,6 +1114,57 @@ class SyntheticPasswordManager {
}
}
+ /**
+ * Writes the user's synthetic password data to the repair mode file.
+ *
+ * @param protectorId current LSKF based protectorId
+ * @param userId user id of the user
+ */
+ public boolean writeRepairModeCredentialLocked(long protectorId, int userId) {
+ if (!shouldWriteRepairModeCredential(userId)) {
+ return false;
+ }
+ final byte[] data = loadState(PASSWORD_DATA_NAME, protectorId, userId);
+ if (data == null) {
+ Slogf.w(TAG, "Password data not found for user %d", userId);
+ return false;
+ }
+ final PasswordData pwd = PasswordData.fromBytes(data);
+ if (isNoneCredential(pwd)) {
+ Slogf.w(TAG, "User %d has NONE credential", userId);
+ return false;
+ }
+ Slogf.d(TAG, "Writing repair mode credential tied to user %d", userId);
+ final int weaverSlot = loadWeaverSlot(protectorId, userId);
+ if (weaverSlot != INVALID_WEAVER_SLOT) {
+ // write weaver password
+ mStorage.writeRepairModePersistentData(
+ PersistentData.TYPE_SP_WEAVER, weaverSlot, pwd.toBytes());
+ } else {
+ // write gatekeeper password
+ mStorage.writeRepairModePersistentData(
+ PersistentData.TYPE_SP_GATEKEEPER, userId, pwd.toBytes());
+ }
+ return true;
+ }
+
+ private boolean shouldWriteRepairModeCredential(int userId) {
+ final UserInfo userInfo = mUserManager.getUserInfo(userId);
+ if (!LockPatternUtils.canUserEnterRepairMode(mContext, userInfo)) {
+ Slogf.w(TAG, "User %d can't enter repair mode", userId);
+ return false;
+ }
+ if (LockPatternUtils.isRepairModeActive(mContext)) {
+ Slog.w(TAG, "Can't write repair mode credential while repair mode is already active");
+ return false;
+ }
+ if (LockPatternUtils.isGsiRunning()) {
+ Slog.w(TAG, "Can't write repair mode credential while GSI is running");
+ return false;
+ }
+ return true;
+ }
+
private ArrayMap<Integer, ArrayMap<Long, TokenData>> tokenMap = new ArrayMap<>();
/**
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9f16a8441533..788bfbcd65c9 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -223,6 +223,7 @@ import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
+import static com.android.server.wm.LetterboxConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
import static com.android.server.wm.LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
@@ -2425,7 +2426,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
allowTaskSnapshot, activityCreated, activityAllDrawn, snapshot);
- //TODO(191787740) Remove for T+
+ //TODO(191787740) Remove for V+
final boolean useLegacy = type == STARTING_WINDOW_TYPE_SPLASH_SCREEN
&& mWmService.mStartingSurfaceController.isExceptionApp(packageName, mTargetSdk,
() -> {
@@ -4550,6 +4551,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* immediately finishes after, so we have to transfer T to M.
*/
void transferStartingWindowFromHiddenAboveTokenIfNeeded() {
+ final WindowState mainWin = findMainWindow(false);
+ if (mainWin != null && mainWin.mWinAnimator.getShown()) {
+ // This activity already has a visible window, so doesn't need to transfer the starting
+ // window from above activity to here. The starting window will be removed with above
+ // activity.
+ return;
+ }
task.forAllActivities(fromActivity -> {
if (fromActivity == this) return true;
return !fromActivity.isVisibleRequested() && transferStartingWindow(fromActivity);
@@ -8294,6 +8302,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
mIsAspectRatioApplied = false;
+ mIsEligibleForFixedOrientationLetterbox = false;
+ mLetterboxBoundsForFixedOrientationAndAspectRatio = null;
// Can't use resolvedConfig.windowConfiguration.getWindowingMode() because it can be
// different from windowing mode of the task (PiP) during transition from fullscreen to PiP
@@ -8303,8 +8313,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final boolean isFixedOrientationLetterboxAllowed =
parentWindowingMode == WINDOWING_MODE_MULTI_WINDOW
|| parentWindowingMode == WINDOWING_MODE_FULLSCREEN
- // Switching from PiP to fullscreen.
- || (parentWindowingMode == WINDOWING_MODE_PINNED
+ // When starting to switch between PiP and fullscreen, the task is pinned
+ // and the activity is fullscreen. But only allow to apply letterbox if the
+ // activity is exiting PiP because an entered PiP should fill the task.
+ || (!mWaitForEnteringPinnedMode
+ && parentWindowingMode == WINDOWING_MODE_PINNED
&& resolvedConfig.windowConfiguration.getWindowingMode()
== WINDOWING_MODE_FULLSCREEN);
// TODO(b/181207944): Consider removing the if condition and always run
@@ -8698,8 +8711,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* in this method.
*/
private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig) {
- mLetterboxBoundsForFixedOrientationAndAspectRatio = null;
- mIsEligibleForFixedOrientationLetterbox = false;
final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
final Rect stableBounds = new Rect();
// If orientation is respected when insets are applied, then stableBounds will be empty.
@@ -8784,9 +8795,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final float letterboxAspectRatioOverride =
mLetterboxUiController.getFixedOrientationLetterboxAspectRatio(newParentConfig);
- final float desiredAspectRatio =
- letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
- ? letterboxAspectRatioOverride : computeAspectRatio(parentBounds);
+
+ // Aspect ratio as suggested by the system. Apps requested mix/max aspect ratio will
+ // be respected in #applyAspectRatio.
+ final float desiredAspectRatio;
+ if (isDefaultMultiWindowLetterboxAspectRatioDesired(newParentConfig)) {
+ desiredAspectRatio = DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
+ } else if (letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO) {
+ desiredAspectRatio = letterboxAspectRatioOverride;
+ } else {
+ desiredAspectRatio = computeAspectRatio(parentBounds);
+ }
+
// Apply aspect ratio to resolved bounds
mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingBoundsWithInsets,
containingBounds, desiredAspectRatio);
@@ -8812,6 +8832,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
/**
+ * Returns {@code true} if the default aspect ratio for a letterboxed app in multi-window mode
+ * should be used.
+ */
+ private boolean isDefaultMultiWindowLetterboxAspectRatioDesired(
+ @NonNull Configuration parentConfig) {
+ if (mDisplayContent == null) {
+ return false;
+ }
+ final int windowingMode = parentConfig.windowConfiguration.getWindowingMode();
+ return WindowConfiguration.inMultiWindowMode(windowingMode)
+ && !mDisplayContent.getIgnoreOrientationRequest();
+ }
+
+ /**
* Resolves aspect ratio restrictions for an activity. If the bounds are restricted by
* aspect ratio, the position will be adjusted later in {@link #updateResolvedBoundsPosition
* within parent's app bounds to balance the visual appearance. The policy of aspect ratio has
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7fc86b0fdc01..ef19eef22794 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -244,6 +244,7 @@ import android.view.inputmethod.ImeTracker;
import android.window.DisplayWindowPolicyController;
import android.window.IDisplayAreaOrganizer;
import android.window.ScreenCapture;
+import android.window.ScreenCapture.SynchronousScreenCaptureListener;
import android.window.TransitionRequestInfo;
import com.android.internal.R;
@@ -5088,7 +5089,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return null;
}
- Pair<ScreenCapture.ScreenCaptureListener, ScreenCapture.ScreenshotSync> syncScreenCapture =
+ SynchronousScreenCaptureListener syncScreenCapture =
ScreenCapture.createSyncCaptureListener();
getBounds(mTmpRect);
@@ -5097,10 +5098,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
new ScreenCapture.LayerCaptureArgs.Builder(getSurfaceControl())
.setSourceCrop(mTmpRect).build();
- ScreenCapture.captureLayers(args, syncScreenCapture.first);
+ ScreenCapture.captureLayers(args, syncScreenCapture);
final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
- syncScreenCapture.second.get();
+ syncScreenCapture.getBuffer();
final Bitmap bitmap = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
if (bitmap == null) {
Slog.w(TAG_WM, "Failed to take screenshot");
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index a93cb8ad2d97..067a18c88aaa 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -52,6 +52,9 @@ final class LetterboxConfiguration {
*/
static final float MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO = 1.0f;
+ /** The default aspect ratio for a letterboxed app in multi-window mode. */
+ static final float DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW = 1.01f;
+
/** Letterboxed app window position multiplier indicating center position. */
static final float LETTERBOX_POSITION_MULTIPLIER_CENTER = 0.5f;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index bc7fa31125f9..54fec3e2f844 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2013,7 +2013,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// from doing work and changing the activity visuals while animating
// TODO(task-org): Figure-out more structured way to do this long term.
r.setWindowingMode(r.getWindowingMode());
- r.mWaitForEnteringPinnedMode = true;
final TaskFragment organizedTf = r.getOrganizedTaskFragment();
final boolean singleActivity = task.getNonFinishingActivityCount() == 1;
@@ -2140,6 +2139,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
rootTask.setDeferTaskAppear(false);
+ // After setting this, it is not expected to change activity configuration until the
+ // transition animation is finished. So the activity can keep consistent appearance
+ // when animating.
+ r.mWaitForEnteringPinnedMode = true;
// Reset the state that indicates it can enter PiP while pausing after we've moved it
// to the root pinned task
r.supportsEnterPipOnTaskSwitch = false;
@@ -3310,9 +3313,14 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (aOptions != null) {
// Resolve the root task the task should be placed in now based on options
// and reparent if needed.
+ // TODO(b/229927851) For split-screen, setLaunchRootTask is no longer the "root"
+ // task, consider to rename methods like "parentTask" instead of "rootTask".
final Task targetRootTask =
getOrCreateRootTask(null, aOptions, task, onTop);
- if (targetRootTask != null && task.getRootTask() != targetRootTask) {
+ // When launch with ActivityOptions#getLaunchRootTask, the "root task" just mean the
+ // parent of current launch, not the "root task" in hierarchy.
+ if (targetRootTask != null && task.getRootTask() != targetRootTask
+ && task.getParent() != targetRootTask) {
final int reparentMode = onTop
? REPARENT_MOVE_ROOT_TASK_TO_FRONT : REPARENT_LEAVE_ROOT_TASK_IN_PLACE;
task.reparent(targetRootTask, onTop, reparentMode, ANIMATE, DEFER_RESUME,
diff --git a/services/core/java/com/android/server/wm/SplashScreenExceptionList.java b/services/core/java/com/android/server/wm/SplashScreenExceptionList.java
index b3cd3f0df97e..3fb897b5c0e9 100644
--- a/services/core/java/com/android/server/wm/SplashScreenExceptionList.java
+++ b/services/core/java/com/android/server/wm/SplashScreenExceptionList.java
@@ -70,7 +70,7 @@ class SplashScreenExceptionList {
}
/**
- * Returns true if the packageName is in the list and the target sdk is before or including T.
+ * Returns true if the packageName is in the list and the target sdk is before or including V.
*
* @param packageName The package name of the application to check
* @param targetSdk The target sdk of the application
@@ -82,7 +82,7 @@ class SplashScreenExceptionList {
@SuppressWarnings("AndroidFrameworkCompatChange") // Target sdk check
public boolean isException(@NonNull String packageName, int targetSdk,
@Nullable Supplier<ApplicationInfo> infoSupplier) {
- if (targetSdk > Build.VERSION_CODES.TIRAMISU) {
+ if (targetSdk > Build.VERSION_CODES.UPSIDE_DOWN_CAKE + 1) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b52935e9da9b..a1473b198cfd 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3887,15 +3887,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
/**
- * @return {@code true} if activity bounds are letterboxed or letterboxed for display cutout.
- * Note that it's always {@code false} if the activity is in pip mode.
+ * Returns {@code true} if activity bounds are letterboxed or letterboxed for display cutout.
*
* <p>Note that letterbox UI may not be shown even when this returns {@code true}. See {@link
* LetterboxUiController#shouldShowLetterboxUi} for more context.
*/
boolean areAppWindowBoundsLetterboxed() {
return mActivityRecord != null
- && !mActivityRecord.inPinnedWindowingMode()
&& (mActivityRecord.areBoundsLetterboxed() || isLetterboxedForDisplayCutout());
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index efd82fa1136d..4f98dca660df 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -646,14 +646,16 @@ public final class DisplayPowerController2Test {
@Test
public void testSetScreenOffBrightnessSensorEnabled_DisplayIsInDoze() {
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, false);
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+
Settings.System.putInt(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS_MODE,
Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
DisplayPowerRequest dpr = new DisplayPowerRequest();
dpr.policy = DisplayPowerRequest.POLICY_DOZE;
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true);
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
advanceTime(1); // Run updatePowerState
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
index 31a58cd67d3e..d1d6e9d41b1f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
@@ -36,7 +36,6 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
-import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -73,7 +72,7 @@ public class FaceProviderTest {
private SensorProps[] mSensorProps;
private LockoutResetDispatcher mLockoutResetDispatcher;
- private TestableFaceProvider mFaceProvider;
+ private FaceProvider mFaceProvider;
private static void waitForIdle() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -98,8 +97,9 @@ public class FaceProviderTest {
mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
- mFaceProvider = new TestableFaceProvider(mDaemon, mContext, mBiometricStateCallback,
- mSensorProps, TAG, mLockoutResetDispatcher, mBiometricContext);
+ mFaceProvider = new FaceProvider(mContext, mBiometricStateCallback,
+ mSensorProps, TAG, mLockoutResetDispatcher, mBiometricContext,
+ mDaemon);
}
@Test
@@ -130,6 +130,7 @@ public class FaceProviderTest {
for (SensorProps prop : mSensorProps) {
final BiometricScheduler scheduler =
mFaceProvider.mFaceSensors.get(prop.commonProps.sensorId).getScheduler();
+ scheduler.reset();
for (int i = 0; i < numFakeOperations; i++) {
final HalClientMonitor testMonitor = mock(HalClientMonitor.class);
when(testMonitor.getFreshDaemon()).thenReturn(new Object());
@@ -142,7 +143,7 @@ public class FaceProviderTest {
for (SensorProps prop : mSensorProps) {
final BiometricScheduler scheduler =
mFaceProvider.mFaceSensors.get(prop.commonProps.sensorId).getScheduler();
- assertEquals(numFakeOperations, scheduler.getCurrentPendingCount());
+ assertEquals(numFakeOperations - 1, scheduler.getCurrentPendingCount());
assertNotNull(scheduler.getCurrentClient());
}
@@ -159,25 +160,4 @@ public class FaceProviderTest {
assertEquals(0, scheduler.getCurrentPendingCount());
}
}
-
- private static class TestableFaceProvider extends FaceProvider {
- private final IFace mDaemon;
-
- TestableFaceProvider(@NonNull IFace daemon,
- @NonNull Context context,
- @NonNull BiometricStateCallback biometricStateCallback,
- @NonNull SensorProps[] props,
- @NonNull String halInstanceName,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull BiometricContext biometricContext) {
- super(context, biometricStateCallback, props, halInstanceName, lockoutResetDispatcher,
- biometricContext);
- mDaemon = daemon;
- }
-
- @Override
- synchronized IFace getHalInstance() {
- return mDaemon;
- }
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
index 9c01de6f0461..8f6efffcbff8 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
@@ -39,7 +39,6 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
-import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -81,7 +80,7 @@ public class FingerprintProviderTest {
private SensorProps[] mSensorProps;
private LockoutResetDispatcher mLockoutResetDispatcher;
- private TestableFingerprintProvider mFingerprintProvider;
+ private FingerprintProvider mFingerprintProvider;
private static void waitForIdle() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -110,17 +109,13 @@ public class FingerprintProviderTest {
mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
- mFingerprintProvider = new TestableFingerprintProvider(mDaemon, mContext,
+ mFingerprintProvider = new FingerprintProvider(mContext,
mBiometricStateCallback, mSensorProps, TAG, mLockoutResetDispatcher,
- mGestureAvailabilityDispatcher, mBiometricContext);
+ mGestureAvailabilityDispatcher, mBiometricContext, mDaemon);
}
@Test
public void testAddingSensors() {
- mFingerprintProvider = new TestableFingerprintProvider(mDaemon, mContext,
- mBiometricStateCallback, mSensorProps, TAG, mLockoutResetDispatcher,
- mGestureAvailabilityDispatcher, mBiometricContext);
-
waitForIdle();
for (SensorProps prop : mSensorProps) {
@@ -147,6 +142,7 @@ public class FingerprintProviderTest {
final BiometricScheduler scheduler =
mFingerprintProvider.mFingerprintSensors.get(prop.commonProps.sensorId)
.getScheduler();
+ scheduler.reset();
for (int i = 0; i < numFakeOperations; i++) {
final HalClientMonitor testMonitor = mock(HalClientMonitor.class);
when(testMonitor.getFreshDaemon()).thenReturn(new Object());
@@ -160,7 +156,7 @@ public class FingerprintProviderTest {
final BiometricScheduler scheduler =
mFingerprintProvider.mFingerprintSensors.get(prop.commonProps.sensorId)
.getScheduler();
- assertEquals(numFakeOperations, scheduler.getCurrentPendingCount());
+ assertEquals(numFakeOperations - 1, scheduler.getCurrentPendingCount());
assertNotNull(scheduler.getCurrentClient());
}
@@ -178,26 +174,4 @@ public class FingerprintProviderTest {
assertEquals(0, scheduler.getCurrentPendingCount());
}
}
-
- private static class TestableFingerprintProvider extends FingerprintProvider {
- private final IFingerprint mDaemon;
-
- TestableFingerprintProvider(@NonNull IFingerprint daemon,
- @NonNull Context context,
- @NonNull BiometricStateCallback biometricStateCallback,
- @NonNull SensorProps[] props,
- @NonNull String halInstanceName,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
- @NonNull BiometricContext biometricContext) {
- super(context, biometricStateCallback, props, halInstanceName, lockoutResetDispatcher,
- gestureAvailabilityDispatcher, biometricContext);
- mDaemon = daemon;
- }
-
- @Override
- synchronized IFingerprint getHalInstance() {
- return mDaemon;
- }
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/input/KeyboardMetricsCollectorTests.kt b/services/tests/servicestests/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
new file mode 100644
index 000000000000..c9724a3b4309
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2023 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.server.input
+
+import android.hardware.input.KeyboardLayout
+import android.icu.util.ULocale
+import android.platform.test.annotations.Presubmit
+import android.view.InputDevice
+import android.view.inputmethod.InputMethodSubtype
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertThrows
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+private fun createKeyboard(
+ deviceId: Int,
+ vendorId: Int,
+ productId: Int,
+ languageTag: String?,
+ layoutType: String?
+): InputDevice =
+ InputDevice.Builder()
+ .setId(deviceId)
+ .setName("Device $deviceId")
+ .setDescriptor("descriptor $deviceId")
+ .setSources(InputDevice.SOURCE_KEYBOARD)
+ .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC)
+ .setExternal(true)
+ .setVendorId(vendorId)
+ .setProductId(productId)
+ .setKeyboardLanguageTag(languageTag)
+ .setKeyboardLayoutType(layoutType)
+ .build()
+
+private fun createImeSubtype(
+ imeSubtypeId: Int,
+ languageTag: String,
+ layoutType: String
+): InputMethodSubtype =
+ InputMethodSubtype.InputMethodSubtypeBuilder().setSubtypeId(imeSubtypeId)
+ .setPhysicalKeyboardHint(ULocale.forLanguageTag(languageTag), layoutType).build()
+
+/**
+ * Tests for {@link KeyboardMetricsCollector}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:KeyboardMetricsCollectorTests
+ */
+@Presubmit
+class KeyboardMetricsCollectorTests {
+
+ companion object {
+ const val DEVICE_ID = 1
+ const val DEFAULT_VENDOR_ID = 123
+ const val DEFAULT_PRODUCT_ID = 456
+ }
+
+ @Test
+ fun testCreateKeyboardConfigurationEvent_throwsExceptionWithoutAnyLayoutConfiguration() {
+ assertThrows(IllegalStateException::class.java) {
+ KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder(
+ createKeyboard(
+ DEVICE_ID,
+ DEFAULT_VENDOR_ID,
+ DEFAULT_PRODUCT_ID,
+ null,
+ null
+ )
+ ).build()
+ }
+ }
+
+ @Test
+ fun testCreateKeyboardConfigurationEvent_throwsExceptionWithInvalidLayoutSelectionCriteria() {
+ assertThrows(IllegalStateException::class.java) {
+ KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder(
+ createKeyboard(
+ DEVICE_ID,
+ DEFAULT_VENDOR_ID,
+ DEFAULT_PRODUCT_ID,
+ null,
+ null
+ )
+ ).addLayoutSelection(createImeSubtype(1, "en-US", "qwerty"), null, 123).build()
+ }
+ }
+
+ @Test
+ fun testCreateKeyboardConfigurationEvent_withMultipleConfigurations() {
+ val builder = KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder(
+ createKeyboard(
+ DEVICE_ID,
+ DEFAULT_VENDOR_ID,
+ DEFAULT_PRODUCT_ID,
+ "de-CH",
+ "qwertz"
+ )
+ )
+ val event = builder.addLayoutSelection(
+ createImeSubtype(1, "en-US", "qwerty"),
+ KeyboardLayout(null, "English(US)(Qwerty)", null, 0, null, 0, 0, 0),
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
+ ).addLayoutSelection(
+ createImeSubtype(2, "en-US", "azerty"),
+ null,
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER
+ ).addLayoutSelection(
+ createImeSubtype(3, "en-US", "qwerty"),
+ KeyboardLayout(null, "German", null, 0, null, 0, 0, 0),
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE
+ ).setIsFirstTimeConfiguration(true).build()
+
+ assertEquals(
+ "KeyboardConfigurationEvent should pick vendor ID from provided InputDevice",
+ DEFAULT_VENDOR_ID,
+ event.vendorId
+ )
+ assertEquals(
+ "KeyboardConfigurationEvent should pick product ID from provided InputDevice",
+ DEFAULT_PRODUCT_ID,
+ event.productId
+ )
+ assertTrue(event.isFirstConfiguration)
+
+ assertEquals(
+ "KeyboardConfigurationEvent should contain 3 configurations provided",
+ 3,
+ event.layoutConfigurations.size
+ )
+ assertExpectedLayoutConfiguration(
+ event.layoutConfigurations[0],
+ "en-US",
+ KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwerty"),
+ "English(US)(Qwerty)",
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
+ )
+ assertExpectedLayoutConfiguration(
+ event.layoutConfigurations[1],
+ "en-US",
+ KeyboardLayout.LayoutType.getLayoutTypeEnumValue("azerty"),
+ KeyboardMetricsCollector.DEFAULT_LAYOUT,
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER
+ )
+ assertExpectedLayoutConfiguration(
+ event.layoutConfigurations[2],
+ "de-CH",
+ KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwertz"),
+ "German",
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE
+ )
+ }
+
+ private fun assertExpectedLayoutConfiguration(
+ configuration: KeyboardMetricsCollector.LayoutConfiguration,
+ expectedLanguageTag: String,
+ expectedLayoutType: Int,
+ expectedSelectedLayout: String,
+ expectedLayoutSelectionCriteria: Int
+ ) {
+ assertEquals(expectedLanguageTag, configuration.keyboardLanguageTag)
+ assertEquals(expectedLayoutType, configuration.keyboardLayoutType)
+ assertEquals(expectedSelectedLayout, configuration.keyboardLayoutName)
+ assertEquals(expectedLayoutSelectionCriteria, configuration.layoutSelectionCriteria)
+ }
+} \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index e960e995f6ce..fe2ac176949d 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -254,6 +254,8 @@ public abstract class BaseLockSettingsServiceTests {
.thenReturn(true);
when(res.getBoolean(eq(com.android.internal.R.bool.config_strongAuthRequiredOnBoot)))
.thenReturn(true);
+ when(res.getBoolean(eq(com.android.internal.R.bool.config_repairModeSupported)))
+ .thenReturn(true);
return res;
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
index 36dc6c5f9f95..a029db922372 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
@@ -84,6 +84,11 @@ public class LockSettingsStorageTestable extends LockSettingsStorage {
}
@Override
+ File getRepairModePersistentDataFile() {
+ return remapToStorageDir(super.getRepairModePersistentDataFile());
+ }
+
+ @Override
PersistentDataBlockManagerInternal getPersistentDataBlockManager() {
return mPersistentDataBlockManager;
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
index 10ed882f343f..23f14f8468bb 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
@@ -457,6 +457,31 @@ public class LockSettingsStorageTests {
assertEquals(2, PersistentData.TYPE_SP_WEAVER);
}
+ @Test
+ public void testRepairMode_emptyPersistentData() {
+ assertSame(PersistentData.NONE, mStorage.readPersistentDataBlock());
+ }
+
+ @Test
+ public void testRepairMode_writeGatekeeperPersistentData() {
+ mStorage.writeRepairModePersistentData(
+ PersistentData.TYPE_SP_GATEKEEPER, SOME_USER_ID, PAYLOAD);
+
+ final PersistentData data = mStorage.readRepairModePersistentData();
+ assertEquals(PersistentData.TYPE_SP_GATEKEEPER, data.type);
+ assertArrayEquals(PAYLOAD, data.payload);
+ }
+
+ @Test
+ public void testRepairMode_writeWeaverPersistentData() {
+ mStorage.writeRepairModePersistentData(
+ PersistentData.TYPE_SP_WEAVER, SOME_USER_ID, PAYLOAD);
+
+ final PersistentData data = mStorage.readRepairModePersistentData();
+ assertEquals(PersistentData.TYPE_SP_WEAVER, data.type);
+ assertArrayEquals(PAYLOAD, data.payload);
+ }
+
private static void assertArrayEquals(byte[] expected, byte[] actual) {
if (!Arrays.equals(expected, actual)) {
fail("expected:<" + Arrays.toString(expected) +
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java
new file mode 100644
index 000000000000..dc47b87cad2d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2023 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.server.locksettings;
+
+import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import android.app.PropertyInvalidatedCache;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.VerifyCredentialResponse;
+import com.android.server.locksettings.LockSettingsStorage.PersistentData;
+import com.android.server.locksettings.SyntheticPasswordManager.PasswordData;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class LockscreenRepairModeTest extends BaseLockSettingsServiceTests {
+
+ @Before
+ public void setUp() throws Exception {
+ PropertyInvalidatedCache.disableForTestMode();
+ mService.initializeSyntheticPassword(PRIMARY_USER_ID);
+ }
+
+ @Test
+ public void verifyPin_writeRepairModePW() {
+ mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID);
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(
+ newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW)
+ .getResponseCode());
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PIN,
+ getCredentialType(mStorage.readRepairModePersistentData()));
+ }
+
+ @Test
+ public void verifyPattern_writeRepairModePW() {
+ mService.setLockCredential(newPattern("4321"), nonePassword(), PRIMARY_USER_ID);
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(
+ newPattern("4321"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW)
+ .getResponseCode());
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+ getCredentialType(mStorage.readRepairModePersistentData()));
+ }
+
+ @Test
+ public void verifyPassword_writeRepairModePW() {
+ mService.setLockCredential(newPassword("4321"), nonePassword(), PRIMARY_USER_ID);
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(
+ newPassword("4321"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW)
+ .getResponseCode());
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ getCredentialType(mStorage.readRepairModePersistentData()));
+ }
+
+ @Test
+ public void verifyCredential_writeRepairModePW_repairModeActive() {
+ mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID);
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+
+ setRepairModeActive(true);
+ assertEquals(VerifyCredentialResponse.RESPONSE_ERROR,
+ mService.verifyCredential(
+ newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW)
+ .getResponseCode());
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+ }
+
+ @Test
+ public void deleteRepairModePersistentData() {
+ mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID);
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(
+ newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW)
+ .getResponseCode());
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PIN,
+ getCredentialType(mStorage.readRepairModePersistentData()));
+
+ mService.deleteRepairModePersistentDataIfNeeded();
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+ }
+
+ private void setRepairModeActive(boolean active) {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.REPAIR_MODE_ACTIVE, active ? 1 : 0);
+ }
+
+ private static int getCredentialType(PersistentData persistentData) {
+ if (persistentData == null || persistentData.payload == null) {
+ return LockPatternUtils.CREDENTIAL_TYPE_NONE;
+ }
+ return PasswordData.fromBytes(persistentData.payload).credentialType;
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 41fcd6935567..3db53eb08ea1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2784,8 +2784,12 @@ public class ActivityRecordTests extends WindowTestsBase {
testLegacySplashScreen(Build.VERSION_CODES.S, TYPE_PARAMETER_LEGACY_SPLASH_SCREEN);
testLegacySplashScreen(Build.VERSION_CODES.TIRAMISU,
TYPE_PARAMETER_LEGACY_SPLASH_SCREEN);
- // Above T
- testLegacySplashScreen(Build.VERSION_CODES.TIRAMISU + 1, 0);
+ testLegacySplashScreen(Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ TYPE_PARAMETER_LEGACY_SPLASH_SCREEN);
+ testLegacySplashScreen(Build.VERSION_CODES.UPSIDE_DOWN_CAKE + 1,
+ TYPE_PARAMETER_LEGACY_SPLASH_SCREEN);
+ // Above V
+ testLegacySplashScreen(Build.VERSION_CODES.UPSIDE_DOWN_CAKE + 2, 0);
} finally {
try {
DeviceConfig.setProperties(properties);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index d173ce9d522a..3bc6450ae591 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -337,6 +337,17 @@ public class RootWindowContainerTests extends WindowTestsBase {
// Ensure a task has moved over.
ensureTaskPlacement(task, activity);
assertTrue(task.inPinnedWindowingMode());
+
+ // The activity with fixed orientation should not apply letterbox when entering PiP.
+ final int requestedOrientation = task.getConfiguration().orientation
+ == Configuration.ORIENTATION_PORTRAIT
+ ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+ doReturn(requestedOrientation).when(activity).getRequestedConfigurationOrientation();
+ doReturn(false).when(activity).handlesOrientationChangeFromDescendant(anyInt());
+ final Rect bounds = new Rect(task.getBounds());
+ bounds.scale(0.5f);
+ task.setBounds(bounds);
+ assertFalse(activity.isLetterboxedForFixedOrientationAndAspectRatio());
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
index 1ee0959447a6..b181213df003 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
@@ -43,16 +43,14 @@ import android.os.Handler;
import android.os.Looper;
import android.os.ServiceManager;
import android.platform.test.annotations.Presubmit;
-import android.util.Pair;
import android.view.IWindowManager;
import android.view.PointerIcon;
import android.view.SurfaceControl;
import android.view.cts.surfacevalidator.BitmapPixelChecker;
import android.view.cts.surfacevalidator.SaveBitmapHelper;
import android.window.ScreenCapture;
-import android.window.ScreenCapture.ScreenCaptureListener;
import android.window.ScreenCapture.ScreenshotHardwareBuffer;
-import android.window.ScreenCapture.ScreenshotSync;
+import android.window.ScreenCapture.SynchronousScreenCaptureListener;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
@@ -169,10 +167,10 @@ public class ScreenshotTests {
.setPosition(sc, point.x, point.y)
.apply(true);
- Pair<ScreenCaptureListener, ScreenshotSync> syncScreenCapture =
+ SynchronousScreenCaptureListener syncScreenCapture =
ScreenCapture.createSyncCaptureListener();
- windowManager.captureDisplay(DEFAULT_DISPLAY, null, syncScreenCapture.first);
- ScreenshotHardwareBuffer hardwareBuffer = syncScreenCapture.second.get();
+ windowManager.captureDisplay(DEFAULT_DISPLAY, null, syncScreenCapture);
+ ScreenshotHardwareBuffer hardwareBuffer = syncScreenCapture.getBuffer();
assertNotNull(hardwareBuffer);
Bitmap screenshot = hardwareBuffer.asBitmap();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 27e6e31ec152..3908947804cd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -2047,6 +2047,72 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ public void testDefaultLetterboxAspectRatioForMultiWindowMode_fixedOrientationApp() {
+ // Set-up display in portrait.
+ mAtm.mDevEnableNonResizableMultiWindow = true;
+ final int screenWidth = 1100;
+ final int screenHeight = 2100;
+ setUpDisplaySizeWithApp(screenWidth, screenHeight);
+
+ mActivity.mDisplayContent.getWindowConfiguration()
+ .setAppBounds(/* left */ 0, /* top */ 0, screenWidth, screenHeight);
+
+ final TestSplitOrganizer organizer =
+ new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+ // Move activity to multi-window which takes half of the screen.
+ mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
+ organizer.mPrimary.setBounds(0, 0, screenWidth, getExpectedSplitSize(screenHeight));
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
+
+ // Unresizable portrait-only activity.
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+ // Activity should be letterboxed with an aspect ratio of 1.01.
+ final Rect afterBounds = mActivity.getBounds();
+ final float actualAspectRatio = 1f * afterBounds.height() / afterBounds.width();
+ assertEquals(LetterboxConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW,
+ actualAspectRatio, 0.001f);
+ assertTrue(mActivity.areBoundsLetterboxed());
+ }
+
+ @Test
+ public void
+ testDefaultLetterboxAspectRatioForMultiWindowMode_fixedOrientationAppWithMinRatio() {
+ // Set-up display in portrait.
+ mAtm.mDevEnableNonResizableMultiWindow = true;
+ final int screenWidth = 1100;
+ final int screenHeight = 2100;
+ setUpDisplaySizeWithApp(screenWidth, screenHeight);
+
+ mActivity.mDisplayContent.getWindowConfiguration()
+ .setAppBounds(/* left */ 0, /* top */ 0, screenWidth, screenHeight);
+
+ // Set min aspect ratio to value greater than the default letterbox aspect ratio for
+ // multi-window mode.
+ final float minAspectRatio = 1.2f;
+ mActivity.info.setMinAspectRatio(minAspectRatio);
+
+ final TestSplitOrganizer organizer =
+ new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+ // Move activity to multi-window which takes half of the screen.
+ mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
+ organizer.mPrimary.setBounds(0, 0, screenWidth, getExpectedSplitSize(screenHeight));
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
+
+ // Unresizable portrait-only activity.
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+ // Activity should be letterboxed with the min aspect ratio requested by the app NOT the
+ // default letterbox aspect ratio for multi-window.
+ final Rect afterBounds = mActivity.getBounds();
+ final float actualAspectRatio = 1f * afterBounds.height() / afterBounds.width();
+ assertEquals(minAspectRatio, actualAspectRatio, 0.001f);
+ assertTrue(mActivity.areBoundsLetterboxed());
+ }
+
+ @Test
public void testDisplayIgnoreOrientationRequest_unresizableWithCorrespondingMinAspectRatio() {
// Set up a display in landscape and ignoring orientation request.
setUpDisplaySizeWithApp(2800, 1400);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java b/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java
index 8425844d8042..8bd54731c7c2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java
@@ -90,19 +90,23 @@ public class SplashScreenExceptionListTest {
public void packageFromDeviceConfigIgnored() {
setExceptionListAndWaitForCallback("com.test.nosplashscreen1,com.test.nosplashscreen2");
- // In list, up to T included
+ // In list, up to V included
assertIsException("com.test.nosplashscreen1", VERSION_CODES.R);
assertIsException("com.test.nosplashscreen1", VERSION_CODES.S);
assertIsException("com.test.nosplashscreen1", VERSION_CODES.TIRAMISU);
+ assertIsException("com.test.nosplashscreen1", VERSION_CODES.UPSIDE_DOWN_CAKE);
+ assertIsException("com.test.nosplashscreen1", VERSION_CODES.UPSIDE_DOWN_CAKE + 1);
- // In list, after T
- assertIsNotException("com.test.nosplashscreen2", VERSION_CODES.TIRAMISU + 1);
+ // In list, after V
+ assertIsNotException("com.test.nosplashscreen2", VERSION_CODES.UPSIDE_DOWN_CAKE + 2);
assertIsNotException("com.test.nosplashscreen2", VERSION_CODES.CUR_DEVELOPMENT);
- // Not in list, up to T included
+ // Not in list, up to V included
assertIsNotException("com.test.splashscreen", VERSION_CODES.S);
assertIsNotException("com.test.splashscreen", VERSION_CODES.R);
assertIsNotException("com.test.splashscreen", VERSION_CODES.TIRAMISU);
+ assertIsNotException("com.test.splashscreen", VERSION_CODES.UPSIDE_DOWN_CAKE);
+ assertIsNotException("com.test.splashscreen", VERSION_CODES.UPSIDE_DOWN_CAKE + 1);
}
@Test
@@ -119,15 +123,19 @@ public class SplashScreenExceptionListTest {
assertIsNotException(packageName, VERSION_CODES.R, activityInfo);
assertIsNotException(packageName, VERSION_CODES.S, activityInfo);
assertIsNotException(packageName, VERSION_CODES.TIRAMISU, activityInfo);
+ assertIsNotException(packageName, VERSION_CODES.UPSIDE_DOWN_CAKE, activityInfo);
+ assertIsNotException(packageName, VERSION_CODES.UPSIDE_DOWN_CAKE + 1, activityInfo);
- // Exception up to T
+ // Exception up to V
metaData.putBoolean("android.splashscreen.exception_opt_out", false);
assertIsException(packageName, VERSION_CODES.R, activityInfo);
assertIsException(packageName, VERSION_CODES.S, activityInfo);
assertIsException(packageName, VERSION_CODES.TIRAMISU, activityInfo);
+ assertIsException(packageName, VERSION_CODES.UPSIDE_DOWN_CAKE, activityInfo);
+ assertIsException(packageName, VERSION_CODES.UPSIDE_DOWN_CAKE + 1, activityInfo);
- // No Exception after T
- assertIsNotException(packageName, VERSION_CODES.TIRAMISU + 1, activityInfo);
+ // No Exception after V
+ assertIsNotException(packageName, VERSION_CODES.UPSIDE_DOWN_CAKE + 2, activityInfo);
assertIsNotException(packageName, VERSION_CODES.CUR_DEVELOPMENT, activityInfo);
// Edge Cases
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 4123f8070e36..960b57cb632a 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -118,6 +118,7 @@ import java.util.UUID;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnManagementServiceTest {
+ private static final String CONTEXT_ATTRIBUTION_TAG = "VCN";
private static final String TEST_PACKAGE_NAME =
VcnManagementServiceTest.class.getPackage().getName();
private static final String TEST_PACKAGE_NAME_2 = "TEST_PKG_2";
@@ -177,6 +178,7 @@ public class VcnManagementServiceTest {
0 /* carrierId */,
0 /* profileClass */);
+ private final Context mMockContextWithoutAttributionTag = mock(Context.class);
private final Context mMockContext = mock(Context.class);
private final VcnManagementService.Dependencies mMockDeps =
mock(VcnManagementService.Dependencies.class);
@@ -202,6 +204,10 @@ public class VcnManagementServiceTest {
private final IBinder mMockIBinder = mock(IBinder.class);
public VcnManagementServiceTest() throws Exception {
+ doReturn(mMockContext)
+ .when(mMockContextWithoutAttributionTag)
+ .createAttributionContext(CONTEXT_ATTRIBUTION_TAG);
+
setupSystemService(
mMockContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
setupSystemService(
@@ -249,7 +255,7 @@ public class VcnManagementServiceTest {
doReturn(bundle).when(mConfigReadWriteHelper).readFromDisk();
setupMockedCarrierPrivilege(true);
- mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps);
+ mVcnMgmtSvc = new VcnManagementService(mMockContextWithoutAttributionTag, mMockDeps);
setupActiveSubscription(TEST_UUID_1);
doReturn(mMockIBinder).when(mMockPolicyListener).asBinder();